# Covo API

Version: 0.6.6

Auto-generated OpenAPI definition for all enabled modules.

## Servers
- https://admin-dev.getcovo.com/api – Default environment

## DELETE `/api_keys/keys`

Delete API key

Removes an API key by identifier. The key must belong to the current tenant and fall within the requester organization scope.

Requires features: api_keys.delete

**Tags:** API Keys

**Requires authentication.**

**Features:** api_keys.delete

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. API key identifier to delete |

### Responses

**200** – Key deleted successfully

Content-Type: `application/json`

```json
{
  "success": true
}
```

**400** – Missing or invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Key not found within scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/api_keys/keys?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/api_keys/keys`

List API keys

Returns paginated API keys visible to the current user, including per-key role assignments and organization context.

Requires features: api_keys.view

**Tags:** API Keys

**Requires authentication.**

**Features:** api_keys.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |

### Responses

**200** – Collection of API keys

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "name": "string",
      "description": null,
      "keyPrefix": "string",
      "organizationId": null,
      "organizationName": null,
      "createdAt": "string",
      "lastUsedAt": null,
      "expiresAt": null,
      "roles": [
        {
          "id": "string",
          "name": null
        }
      ]
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**400** – Tenant context missing

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/api_keys/keys" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/api_keys/keys`

Create API key

Creates a new API key, returning the one-time secret value together with the generated key prefix and scope details.

Requires features: api_keys.create

**Tags:** API Keys

**Requires authentication.**

**Features:** api_keys.create

### Request Body

Content-Type: `application/json`

```json
{
  "name": "string",
  "description": null,
  "tenantId": null,
  "organizationId": null,
  "roles": [],
  "expiresAt": null
}
```

### Responses

**201** – API key created successfully

Content-Type: `application/json`

```json
{
  "id": "string",
  "name": "string",
  "keyPrefix": "string",
  "tenantId": null,
  "organizationId": null,
  "roles": [
    {
      "id": "string",
      "name": null
    }
  ]
}
```

**400** – Invalid payload or missing tenant context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/api_keys/keys" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\",
  \"description\": null,
  \"tenantId\": null,
  \"organizationId\": null,
  \"roles\": [],
  \"expiresAt\": null
}"
```

## DELETE `/attachments`

Delete attachment

Removes an uploaded attachment and deletes the stored asset.

Requires features: attachments.manage

**Tags:** Attachments

**Requires authentication.**

**Features:** attachments.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required |

### Responses

**200** – Attachment deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing attachment identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Attachment not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/attachments?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/attachments`

List attachments for a record

Returns uploaded attachments for the given entity record, ordered by newest first.

Requires features: attachments.view

**Tags:** Attachments

**Requires authentication.**

**Features:** attachments.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Required. Entity identifier that owns the attachments |
| recordId | query | any | Required. Record identifier within the entity |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – Attachments found for the record

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "url": "string",
      "fileName": "string",
      "fileSize": 1,
      "createdAt": "string",
      "mimeType": null,
      "content": null
    }
  ]
}
```

**400** – Missing entity or record identifiers

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/attachments?entityId=string&recordId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/attachments`

Upload attachment

Uploads a new attachment using multipart form-data and stores metadata for later retrieval.

Requires features: attachments.manage

**Tags:** Attachments

**Requires authentication.**

**Features:** attachments.manage

### Request Body

Content-Type: `multipart/form-data`

```text
entityId=string
recordId=string
file=string
```

### Responses

**200** – Attachment stored successfully

Content-Type: `application/json`

```json
{
  "ok": true,
  "item": {
    "id": "string",
    "url": "string",
    "fileName": "string",
    "fileSize": 1,
    "content": null
  }
}
```

**400** – Payload validation error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/attachments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: multipart/form-data" \
  -d "{
  \"entityId\": \"string\",
  \"recordId\": \"string\",
  \"file\": \"string\"
}"
```

## GET `/attachments/file/{id}`

Download or serve attachment file

Returns the raw file content for an attachment. Path parameter: {id} - Attachment UUID. Query parameter: ?download=1 - Force file download with Content-Disposition header. Access control is enforced based on partition settings.

**Tags:** Attachments

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – File content with appropriate MIME type

Content-Type: `application/json`

**400** – Missing attachment ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized - authentication required for private partitions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Attachment or file not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Partition misconfigured

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/attachments/file/:id" \
  -H "Accept: application/json"
```

## GET `/attachments/image/{id}/{slug}`

Serve image with optional resizing

Returns an image attachment with optional on-the-fly resizing and cropping. Resized images are cached for performance. Only works with image MIME types. Path parameter: {id} - Attachment UUID. Query parameters: ?width=N (1-4000 pixels), ?height=N (1-4000 pixels), ?cropType=cover|contain (resize behavior).

**Tags:** Attachments

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| slug | path | any | Optional |

### Responses

**200** – Binary image content (Content-Type: image/jpeg, image/png, etc.)

Content-Type: `application/json`

**400** – Invalid parameters, missing ID, or non-image attachment

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized - authentication required for private partitions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Image not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Partition misconfigured or image rendering failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/attachments/image/:id/:slug" \
  -H "Accept: application/json"
```

## GET `/attachments/library`

List attachments

Returns paginated list of attachments with optional filtering by search term, partition, and tags. Includes available tags and partitions.

Requires features: attachments.view

**Tags:** Attachments

**Requires authentication.**

**Features:** attachments.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional. Page number for pagination |
| pageSize | query | any | Optional. Number of items per page (max 100) |
| search | query | any | Optional. Search by file name (case-insensitive) |
| partition | query | any | Optional. Filter by partition code |
| tags | query | any | Optional. Filter by tags (comma-separated) |
| sortField | query | any | Optional. Field to sort by |
| sortDir | query | any | Optional. Sort direction |

### Responses

**200** – Attachments list with pagination and metadata

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "fileName": "string",
      "fileSize": 1,
      "mimeType": "string",
      "partitionCode": "string",
      "partitionTitle": null,
      "url": null,
      "createdAt": "string",
      "tags": [
        "string"
      ],
      "assignments": [],
      "content": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1,
  "availableTags": [
    "string"
  ],
  "partitions": [
    {
      "code": "string",
      "title": "string",
      "description": null,
      "isPublic": true
    }
  ]
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/attachments/library?page=1&pageSize=25" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/attachments/library/{id}`

Delete attachment

Permanently deletes an attachment file from storage and database. Emits CRUD side effects.

Requires features: attachments.manage

**Tags:** Attachments

**Requires authentication.**

**Features:** attachments.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Attachment deleted successfully

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid attachment ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Attachment not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/attachments/library/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/attachments/library/{id}`

Get attachment details

Returns complete details of an attachment including metadata, tags, assignments, and custom fields.

Requires features: attachments.view

**Tags:** Attachments

**Requires authentication.**

**Features:** attachments.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Attachment details

Content-Type: `application/json`

```json
{
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "fileName": "string",
    "fileSize": 1,
    "mimeType": "string",
    "partitionCode": "string",
    "partitionTitle": null,
    "tags": [
      "string"
    ],
    "assignments": [],
    "content": null,
    "customFields": null
  }
}
```

**400** – Invalid attachment ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Attachment not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/attachments/library/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/attachments/library/{id}`

Update attachment metadata

Updates attachment tags, assignments, and custom fields. Emits CRUD side effects for indexing and events.

Requires features: attachments.manage

**Tags:** Attachments

**Requires authentication.**

**Features:** attachments.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Attachment updated successfully

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload or attachment ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Attachment not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to save attributes

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/attachments/library/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## DELETE `/attachments/partitions`

Delete partition

Deletes a partition. Default partitions cannot be deleted. Partitions with existing attachments cannot be deleted.

Requires features: attachments.manage

**Tags:** Attachments

**Requires authentication.**

**Features:** attachments.manage

### Responses

**200** – Partition deleted successfully

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid ID or default partition deletion attempt

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Partition not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Partition in use

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/attachments/partitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/attachments/partitions`

List all attachment partitions

Returns all configured attachment partitions with storage settings, OCR configuration, and access control settings.

Requires features: attachments.manage

**Tags:** Attachments

**Requires authentication.**

**Features:** attachments.manage

### Responses

**200** – List of partitions

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "code": "string",
      "title": "string",
      "description": null,
      "isPublic": true,
      "requiresOcr": true,
      "ocrModel": null,
      "configJson": null,
      "createdAt": null,
      "updatedAt": null,
      "envKey": "string"
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/attachments/partitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/attachments/partitions`

Create new partition

Creates a new attachment partition with specified storage and OCR settings. Requires unique partition code.

Requires features: attachments.manage

**Tags:** Attachments

**Requires authentication.**

**Features:** attachments.manage

### Request Body

Content-Type: `application/json`

```json
{
  "code": "string",
  "title": "string",
  "description": null,
  "ocrModel": null,
  "storageDriver": "local",
  "configJson": null
}
```

### Responses

**201** – Partition created successfully

Content-Type: `application/json`

```json
{
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "code": "string",
    "title": "string",
    "description": null,
    "isPublic": true,
    "requiresOcr": true,
    "ocrModel": null,
    "configJson": null,
    "createdAt": null,
    "updatedAt": null,
    "envKey": "string"
  }
}
```

**400** – Invalid payload or partition code

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Partition code already exists

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/attachments/partitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"code\": \"string\",
  \"title\": \"string\",
  \"description\": null,
  \"ocrModel\": null,
  \"storageDriver\": \"local\",
  \"configJson\": null
}"
```

## PUT `/attachments/partitions`

Update partition

Updates an existing partition. Partition code cannot be changed. Title, description, OCR settings, and access control can be modified.

Requires features: attachments.manage

**Tags:** Attachments

**Requires authentication.**

**Features:** attachments.manage

### Request Body

Content-Type: `application/json`

```json
{
  "code": "string",
  "title": "string",
  "description": null,
  "ocrModel": null,
  "storageDriver": "local",
  "configJson": null,
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Partition updated successfully

Content-Type: `application/json`

```json
{
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "code": "string",
    "title": "string",
    "description": null,
    "isPublic": true,
    "requiresOcr": true,
    "ocrModel": null,
    "configJson": null,
    "createdAt": null,
    "updatedAt": null,
    "envKey": "string"
  }
}
```

**400** – Invalid payload or code change attempt

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Partition not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/attachments/partitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"code\": \"string\",
  \"title\": \"string\",
  \"description\": null,
  \"ocrModel\": null,
  \"storageDriver\": \"local\",
  \"configJson\": null,
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## POST `/attachments/transfer`

Transfer attachments to different record

Transfers one or more attachments from one record to another within the same entity type. Updates attachment assignments and metadata to reflect the new record.

Requires features: attachments.manage

**Tags:** Attachments

**Requires authentication.**

**Features:** attachments.manage

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "attachmentIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "toRecordId": "string"
}
```

### Responses

**200** – Attachments transferred successfully

Content-Type: `application/json`

```json
{
  "ok": true,
  "updated": 1
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Attachments not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Attachment model missing

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/attachments/transfer" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"attachmentIds\": [
    \"00000000-0000-4000-8000-000000000000\"
  ],
  \"toRecordId\": \"string\"
}"
```

## GET `/audit_logs/audit-logs/access`

Retrieve access logs

Fetches paginated access audit logs scoped to the authenticated user. Tenant administrators can optionally expand the search to other actors or organizations.

Requires features: audit_logs.view_self

**Tags:** Audit & Action Logs

**Requires authentication.**

**Features:** audit_logs.view_self

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| organizationId | query | any | Optional. Limit results to a specific organization |
| actorUserId | query | any | Optional. Filter by actor user id (tenant administrators only) |
| resourceKind | query | any | Optional. Restrict to a resource kind such as `order` or `product` |
| accessType | query | any | Optional. Access type filter, e.g. `read` or `export` |
| page | query | any | Optional. Page number (default 1) |
| pageSize | query | any | Optional. Page size (default 50) |
| limit | query | any | Optional. Explicit maximum number of records when paginating manually |
| before | query | any | Optional. Return logs created before this ISO-8601 timestamp |
| after | query | any | Optional. Return logs created after this ISO-8601 timestamp |

### Responses

**200** – Access logs returned successfully

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "resourceKind": "string",
      "resourceId": "string",
      "accessType": "string",
      "actorUserId": null,
      "actorUserName": null,
      "tenantId": null,
      "tenantName": null,
      "organizationId": null,
      "organizationName": null,
      "fields": [
        "string"
      ],
      "context": null,
      "createdAt": "string"
    }
  ],
  "canViewTenant": true,
  "page": 1,
  "pageSize": 1,
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid filters supplied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/audit_logs/audit-logs/access" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/audit_logs/audit-logs/actions`

Fetch action logs

Returns recent action audit log entries. Tenant administrators can widen the scope to other actors or organizations, and callers can optionally restrict results to undoable actions.

Requires features: audit_logs.view_self

**Tags:** Audit & Action Logs

**Requires authentication.**

**Features:** audit_logs.view_self

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| organizationId | query | any | Optional. Limit results to a specific organization |
| actorUserId | query | any | Optional. Filter logs created by specific actor IDs (tenant administrators only). Accepts a single UUID or a comma-separated UUID list. |
| resourceKind | query | any | Optional. Filter by resource kind (e.g., "order", "product") |
| resourceId | query | any | Optional. Filter by resource ID (UUID of the specific record) |
| actionType | query | any | Optional. Filter by action type (`create`, `edit`, `delete`, `assign`). Accepts a single value or a comma-separated list. |
| fieldName | query | any | Optional. Filter to entries where the given field changed. Accepts a single field name or a comma-separated list. |
| includeRelated | query | any | Optional. When `true`, also returns changes to child entities linked via parentResourceKind/parentResourceId |
| includeTotal | query | any | Optional. When `true`, the response includes the filtered total count. |
| undoableOnly | query | any | Optional. When `true`, only undoable actions are returned |
| limit | query | any | Optional. Maximum number of records to return (default 50, max 1000) |
| offset | query | any | Optional. Zero-based record offset for pagination (legacy — prefer page/pageSize) |
| page | query | any | Optional. Page number (default 1) |
| pageSize | query | any | Optional. Page size (default 50, max 200) |
| sortField | query | any | Optional. Sort field: `createdAt`, `user`, `action`, `field`, or `source`. |
| sortDir | query | any | Optional. Sort direction: `asc` or `desc`. |
| before | query | any | Optional. Return actions created before this ISO-8601 timestamp |
| after | query | any | Optional. Return actions created after this ISO-8601 timestamp |

### Responses

**200** – Action logs retrieved successfully

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "commandId": "string",
      "actionLabel": null,
      "executionState": "done",
      "actorUserId": null,
      "actorUserName": null,
      "tenantId": null,
      "tenantName": null,
      "organizationId": null,
      "organizationName": null,
      "resourceKind": null,
      "resourceId": null,
      "parentResourceKind": null,
      "parentResourceId": null,
      "undoToken": null,
      "createdAt": "string",
      "updatedAt": "string",
      "snapshotBefore": null,
      "snapshotAfter": null,
      "changes": null,
      "context": null
    }
  ],
  "canViewTenant": true,
  "page": 1,
  "pageSize": 1,
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid filter values

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/audit_logs/audit-logs/actions?includeRelated=false&includeTotal=false&undoableOnly=false" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/audit_logs/audit-logs/actions/export`

Export action logs as CSV

Returns a CSV attachment containing filtered action audit log entries. Tenant administrators can widen the scope to other actors or organizations.

Requires features: audit_logs.view_self

**Tags:** Audit & Action Logs

**Requires authentication.**

**Features:** audit_logs.view_self

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| organizationId | query | any | Optional. Limit results to a specific organization |
| actorUserId | query | any | Optional. Filter logs created by specific actor IDs (tenant administrators only). Accepts a single UUID or a comma-separated UUID list. |
| resourceKind | query | any | Optional. Filter by resource kind (e.g., "order", "product") |
| resourceId | query | any | Optional. Filter by resource ID (UUID of the specific record) |
| actionType | query | any | Optional. Filter by action type (`create`, `edit`, `delete`, `assign`). Accepts a single value or a comma-separated list. |
| fieldName | query | any | Optional. Filter to entries where the given field changed. Accepts a single field name or a comma-separated list. |
| includeRelated | query | any | Optional. When `true`, also returns changes to child entities linked via parentResourceKind/parentResourceId |
| undoableOnly | query | any | Optional. When `true`, only undoable actions are returned |
| limit | query | any | Optional. Maximum number of records to export (default 1000, capped at 1000) |
| sortField | query | any | Optional. Sort field: `createdAt`, `user`, `action`, `field`, or `source`. |
| sortDir | query | any | Optional. Sort direction: `asc` or `desc`. |
| before | query | any | Optional. Return actions created before this ISO-8601 timestamp |
| after | query | any | Optional. Return actions created after this ISO-8601 timestamp |

### Responses

**200** – CSV export generated successfully

Content-Type: `application/json`

```json
{
  "file": "csv"
}
```

**400** – Invalid filter values

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/audit_logs/audit-logs/actions/export?includeRelated=false&undoableOnly=false" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/audit_logs/audit-logs/actions/redo`

Redo by action log id

Redoes the latest undone command owned by the caller. Requires the action to still be eligible for redo within tenant and organization scope.

Requires features: audit_logs.redo_self

**Tags:** Audit & Action Logs

**Requires authentication.**

**Features:** audit_logs.redo_self

### Request Body

Content-Type: `application/json`

```json
{
  "logId": "string"
}
```

### Responses

**200** – Redo executed successfully

Content-Type: `application/json`

```json
{
  "ok": true,
  "logId": null,
  "undoToken": null
}
```

**400** – Log not eligible for redo

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/audit_logs/audit-logs/actions/redo" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"logId\": \"string\"
}"
```

## POST `/audit_logs/audit-logs/actions/undo`

Undo action by token

Replays the undo handler registered for a command. The provided undo token must match the latest undoable log entry accessible to the caller.

Requires features: audit_logs.undo_self

**Tags:** Audit & Action Logs

**Requires authentication.**

**Features:** audit_logs.undo_self

### Request Body

Content-Type: `application/json`

```json
{
  "undoToken": "string"
}
```

### Responses

**200** – Undo applied successfully

Content-Type: `application/json`

```json
{
  "ok": true,
  "logId": "string"
}
```

**400** – Invalid or unavailable undo token

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/audit_logs/audit-logs/actions/undo" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"undoToken\": \"string\"
}"
```

## GET `/auth/admin/nav`

Resolve backend chrome bootstrap payload

Returns the backend chrome payload available to the authenticated administrator after applying scope, RBAC, role defaults, and personal sidebar preferences.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Responses

**200** – Backend chrome payload

Content-Type: `application/json`

```json
{
  "brand": null,
  "groups": [
    {
      "name": "string",
      "items": [
        {
          "href": "string",
          "title": "string"
        }
      ]
    }
  ],
  "settingsSections": [
    {
      "id": "string",
      "label": "string",
      "items": [
        {
          "id": "string",
          "label": "string",
          "href": "string"
        }
      ]
    }
  ],
  "settingsPathPrefixes": [
    "string"
  ],
  "profileSections": [
    {
      "id": "string",
      "label": "string",
      "items": [
        {
          "id": "string",
          "label": "string",
          "href": "string"
        }
      ]
    }
  ],
  "profilePathPrefixes": [
    "string"
  ],
  "grantedFeatures": [
    "string"
  ],
  "roles": [
    "string"
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/auth/admin/nav" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/auth/feature-check`

Check feature grants for the current user

Evaluates which of the requested features are available to the signed-in user within the active tenant / organization context.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "features": [
    "string"
  ]
}
```

### Responses

**200** – Evaluation result

Content-Type: `application/json`

```json
{
  "ok": true,
  "granted": [
    "string"
  ],
  "userId": "string"
}
```

**400** – Invalid request — features array missing, too large, or contains invalid entries

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/auth/feature-check" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"features\": [
    \"string\"
  ]
}"
```

## GET `/auth/features`

List declared feature flags

Returns all static features contributed by the enabled modules along with their module source.

Requires features: auth.acl.manage

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.acl.manage

### Responses

**200** – Aggregated feature catalog

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "title": "string",
      "module": "string"
    }
  ],
  "modules": [
    {
      "id": "string",
      "title": "string"
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/auth/features" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/auth/locale`

Set locale and redirect

Stores the selected locale in a cookie and redirects to a safe local path.

**Tags:** Authentication & Accounts

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| locale | query | any | Required |
| redirect | query | any | Optional |

### Responses

**200** – Success response

Content-Type: `application/json`

**302** – Locale cookie set and request redirected

Content-Type: `application/json`

**400** – Invalid locale

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/auth/locale?locale=en" \
  -H "Accept: application/json"
```

## POST `/auth/locale`

Set locale

Stores the selected locale in a cookie and returns a JSON success response.

**Tags:** Authentication & Accounts

### Request Body

Content-Type: `application/json`

```json
{
  "locale": "en"
}
```

### Responses

**200** – Locale cookie set

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid locale or malformed request body

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/auth/locale" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"locale\": \"en\"
}"
```

## POST `/auth/login`

Authenticate user credentials

Validates the submitted credentials and issues a bearer token cookie for subsequent API calls.

**Tags:** Authentication & Accounts

### Request Body

Content-Type: `application/x-www-form-urlencoded`

```text
email=user%40example.com&password=string
```

### Responses

**200** – Authentication succeeded

Content-Type: `application/json`

```json
{
  "ok": true,
  "token": "string",
  "redirect": null
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**401** – Invalid credentials

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**403** – User lacks required role

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**429** – Too many login attempts

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/auth/login" \
  -H "Accept: application/json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "email=user%40example.com&password=string"
```

## POST `/auth/logout`

Invalidate session and redirect

Clears authentication cookies and redirects the browser to the login page.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Responses

**201** – Success response

Content-Type: `application/json`

**302** – Redirect to login after successful logout

Content-Type: `text/html`

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/auth/logout" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/auth/profile`

Get current profile

Returns the email address for the signed-in user.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Responses

**200** – Profile payload

Content-Type: `application/json`

```json
{
  "email": "user@example.com",
  "roles": [
    "string"
  ]
}
```

**404** – User not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/auth/profile" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/auth/profile`

Update current profile

Updates the email address or password for the signed-in user.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Profile updated

Content-Type: `application/json`

```json
{
  "ok": true,
  "email": "user@example.com"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/auth/profile" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/auth/reset`

Send reset email

Requests a password reset email for the given account. The endpoint always returns `ok: true` to avoid leaking account existence.

**Tags:** Authentication & Accounts

### Request Body

Content-Type: `application/x-www-form-urlencoded`

```text
email=user%40example.com
```

### Responses

**200** – Reset email dispatched (or ignored for unknown accounts)

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid request origin

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Too many password reset requests

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Password reset email origin is not configured

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/auth/reset" \
  -H "Accept: application/json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "email=user%40example.com"
```

## POST `/auth/reset/confirm`

Complete password reset

Validates the reset token and updates the user password.

**Tags:** Authentication & Accounts

### Request Body

Content-Type: `application/x-www-form-urlencoded`

```text
token=string&password=string
```

### Responses

**200** – Password reset succeeded

Content-Type: `application/json`

```json
{
  "ok": true,
  "redirect": "string"
}
```

**400** – Invalid token or payload

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**429** – Too many reset confirmation attempts

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/auth/reset/confirm" \
  -H "Accept: application/json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=string&password=string"
```

## DELETE `/auth/roles`

Delete role

Deletes a role by identifier. Fails when users remain assigned.

Requires features: auth.roles.manage

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.roles.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. Role identifier |

### Responses

**200** – Role deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Role cannot be deleted

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/auth/roles?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/auth/roles`

List roles

Returns available roles within the current tenant. Super administrators receive visibility across tenants.

Requires features: auth.roles.list

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.roles.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| tenantId | query | any | Optional |

### Responses

**200** – Role collection

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "usersCount": 1,
      "tenantId": null,
      "tenantName": null,
      "updatedAt": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/auth/roles?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/auth/roles`

Create role

Creates a new role anchored to the caller's tenant. Non-superadmins cannot target another tenant; supplying a foreign `tenantId` is rejected.

Requires features: auth.roles.manage

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.roles.manage

### Request Body

Content-Type: `application/json`

```json
{
  "name": "string"
}
```

### Responses

**201** – Role created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/auth/roles" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\"
}"
```

## PUT `/auth/roles`

Update role

Updates mutable fields on an existing role.

Requires features: auth.roles.manage

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.roles.manage

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Role updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/auth/roles" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/auth/roles/acl`

Fetch role ACL

Returns the feature and organization assignments associated with a role within the current tenant.

Requires features: auth.acl.manage

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.acl.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| roleId | query | any | Required |
| tenantId | query | any | Optional |

### Responses

**200** – Role ACL entry

Content-Type: `application/json`

```json
{
  "isSuperAdmin": true,
  "features": [
    "string"
  ],
  "organizations": null,
  "updatedAt": null
}
```

**400** – Invalid role id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/auth/roles/acl?roleId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/auth/roles/acl`

Update role ACL

Replaces the feature list, super admin flag, and optional organization assignments for a role.

Requires features: auth.acl.manage

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.acl.manage

### Request Body

Content-Type: `application/json`

```json
{
  "roleId": "00000000-0000-4000-8000-000000000000",
  "organizations": null
}
```

### Responses

**200** – Role ACL updated

Content-Type: `application/json`

```json
{
  "ok": true,
  "sanitized": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/auth/roles/acl" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"roleId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizations\": null
}"
```

## GET `/auth/session/refresh`

Refresh auth cookie from session token (browser)

Exchanges an existing `session_token` cookie for a fresh JWT auth cookie and redirects the browser.

**Tags:** Authentication & Accounts

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| redirect | query | any | Optional. Absolute or relative URL to redirect after refresh |

### Responses

**200** – Success response

Content-Type: `application/json`

**302** – Redirect to target location when session is valid

Content-Type: `text/html`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/auth/session/refresh" \
  -H "Accept: application/json"
```

## POST `/auth/session/refresh`

Refresh access token (API/mobile)

Exchanges a refresh token for a new JWT access token. Pass the refresh token obtained from login in the request body.

**Tags:** Authentication & Accounts

### Request Body

Content-Type: `application/json`

```json
{
  "refreshToken": "string"
}
```

### Responses

**200** – New access token issued

Content-Type: `application/json`

```json
{
  "ok": true,
  "accessToken": "string",
  "expiresIn": 1
}
```

**400** – Missing refresh token

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**401** – Invalid or expired token

Content-Type: `application/json`

```json
{
  "ok": false,
  "error": "string"
}
```

**429** – Too many refresh attempts

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/auth/session/refresh" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"refreshToken\": \"string\"
}"
```

## DELETE `/auth/sidebar/preferences`

Delete a role sidebar variant

Removes the role variant for the current tenant + locale. Idempotent. Requires `auth.sidebar.manage`.

Requires features: auth.sidebar.manage

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.sidebar.manage

### Responses

**200** – Variant deleted (or never existed)

Content-Type: `application/json`

```json
{
  "ok": true,
  "scope": {
    "type": "user"
  }
}
```

**400** – Missing roleId query parameter

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found in current tenant scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/auth/sidebar/preferences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/auth/sidebar/preferences`

Get sidebar preferences

Returns sidebar customization for the current user (default) or the specified role (`?roleId=…`, requires `auth.sidebar.manage`).

**Tags:** Authentication & Accounts

**Requires authentication.**

### Responses

**200** – Current sidebar configuration

Content-Type: `application/json`

```json
{
  "locale": "string",
  "settings": {
    "version": 1,
    "groupOrder": [
      "string"
    ],
    "groupLabels": {
      "key": "string"
    },
    "itemLabels": {
      "key": "string"
    },
    "hiddenItems": [
      "string"
    ],
    "itemOrder": {
      "key": [
        "string"
      ]
    }
  },
  "canApplyToRoles": true,
  "roles": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "hasPreference": true
    }
  ],
  "scope": {
    "type": "user"
  },
  "updatedAt": null
}
```

**403** – Missing features for role-scope read

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found in current tenant scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/auth/sidebar/preferences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/auth/sidebar/preferences`

Update sidebar preferences

Updates sidebar configuration. With `scope.type === "user"` (default) writes the calling user's personal preferences and may optionally apply the same settings to selected roles via `applyToRoles[]`. With `scope.type === "role"` writes the named role variant directly (requires `auth.sidebar.manage`); `applyToRoles[]` and `clearRoleIds[]` are rejected in this mode.

Requires features: auth.sidebar.manage

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.sidebar.manage

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Preferences saved

Content-Type: `application/json`

```json
{
  "locale": "string",
  "settings": {
    "version": 1,
    "groupOrder": [
      "string"
    ],
    "groupLabels": {
      "key": "string"
    },
    "itemLabels": {
      "key": "string"
    },
    "hiddenItems": [
      "string"
    ],
    "itemOrder": {
      "key": [
        "string"
      ]
    }
  },
  "canApplyToRoles": true,
  "roles": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "hasPreference": true
    }
  ],
  "scope": {
    "type": "user"
  },
  "updatedAt": null,
  "appliedRoles": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "clearedRoles": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Role not found in current tenant scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/auth/sidebar/preferences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## GET `/auth/sidebar/variants`

List sidebar variants

Returns the named sidebar variants saved by the current user for the current tenant + locale.

**Tags:** Authentication & Accounts

**Requires authentication.**

### Responses

**200** – Variant list

Content-Type: `application/json`

```json
{
  "locale": "string",
  "variants": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "isActive": true,
      "settings": {
        "version": 1,
        "groupOrder": [
          "string"
        ],
        "groupLabels": {
          "key": "string"
        },
        "itemLabels": {
          "key": "string"
        },
        "hiddenItems": [
          "string"
        ],
        "itemOrder": {
          "key": [
            "string"
          ]
        }
      },
      "createdAt": "string",
      "updatedAt": null
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/auth/sidebar/variants" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/auth/sidebar/variants`

Create a sidebar variant

Creates a new variant. If `name` is omitted or blank, an auto-name like "My preferences", "My preferences 2", … is assigned.

Requires features: auth.sidebar.manage

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.sidebar.manage

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Variant created

Content-Type: `application/json`

```json
{
  "locale": "string",
  "variant": {
    "id": "00000000-0000-4000-8000-000000000000",
    "name": "string",
    "isActive": true,
    "settings": {
      "version": 1,
      "groupOrder": [
        "string"
      ],
      "groupLabels": {
        "key": "string"
      },
      "itemLabels": {
        "key": "string"
      },
      "hiddenItems": [
        "string"
      ],
      "itemOrder": {
        "key": [
          "string"
        ]
      }
    },
    "createdAt": "string",
    "updatedAt": null
  }
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/auth/sidebar/variants" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## DELETE `/auth/sidebar/variants/{id}`

Delete a sidebar variant

Soft-deletes the variant (sets deleted_at).

Requires features: auth.sidebar.manage

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.sidebar.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Variant deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**404** – Variant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/auth/sidebar/variants/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/auth/sidebar/variants/{id}`

Get a sidebar variant

**Tags:** Authentication & Accounts

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Variant

Content-Type: `application/json`

```json
{
  "locale": "string",
  "variant": {
    "id": "00000000-0000-4000-8000-000000000000",
    "name": "string",
    "isActive": true,
    "settings": {
      "version": 1,
      "groupOrder": [
        "string"
      ],
      "groupLabels": {
        "key": "string"
      },
      "itemLabels": {
        "key": "string"
      },
      "hiddenItems": [
        "string"
      ],
      "itemOrder": {
        "key": [
          "string"
        ]
      }
    },
    "createdAt": "string",
    "updatedAt": null
  }
}
```

**404** – Variant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/auth/sidebar/variants/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/auth/sidebar/variants/{id}`

Update a sidebar variant

Updates the variant's name, settings, and/or isActive flag. Setting `isActive: true` deactivates other variants in the same scope (only one active per user/tenant/locale).

Requires features: auth.sidebar.manage

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.sidebar.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Variant updated

Content-Type: `application/json`

```json
{
  "locale": "string",
  "variant": {
    "id": "00000000-0000-4000-8000-000000000000",
    "name": "string",
    "isActive": true,
    "settings": {
      "version": 1,
      "groupOrder": [
        "string"
      ],
      "groupLabels": {
        "key": "string"
      },
      "itemLabels": {
        "key": "string"
      },
      "hiddenItems": [
        "string"
      ],
      "itemOrder": {
        "key": [
          "string"
        ]
      }
    },
    "createdAt": "string",
    "updatedAt": null
  }
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Variant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/auth/sidebar/variants/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## DELETE `/auth/users`

Delete user

Deletes a user by identifier. Undo support is provided via the command bus.

Requires features: auth.users.delete

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.users.delete

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. User identifier |

### Responses

**200** – User deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – User cannot be deleted

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – User not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/auth/users?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/auth/users`

List users

Returns users for the effective selected tenant and organization scope. Search matches email, organization name, and role name. Super administrators may scope the response via the topbar context, organization filters, or role filters.

Requires features: auth.users.list

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.users.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| name | query | any | Optional |
| organizationId | query | any | Optional |
| roleIds | query | any | Optional |

### Responses

**200** – User collection

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "email": "user@example.com",
      "name": null,
      "organizationId": null,
      "organizationName": null,
      "tenantId": null,
      "tenantName": null,
      "roles": [
        "string"
      ],
      "updatedAt": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/auth/users?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/auth/users`

Create user

Creates a new confirmed user within the specified organization, optional display name, and optional roles.

Requires features: auth.users.create

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.users.create

### Request Body

Content-Type: `application/json`

```json
{
  "email": "user@example.com",
  "name": null,
  "organizationId": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**201** – User created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid payload or duplicate email

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/auth/users" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"email\": \"user@example.com\",
  \"name\": null,
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## PUT `/auth/users`

Update user

Updates profile fields including display name, organization assignment, credentials, or role memberships.

Requires features: auth.users.edit

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.users.edit

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "name": null
}
```

### Responses

**200** – User updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – User not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/auth/users" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"name\": null
}"
```

## GET `/auth/users/acl`

Fetch user ACL

Returns custom ACL overrides for a user within the current tenant, if any.

Requires features: auth.acl.manage

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.acl.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| userId | query | any | Required |

### Responses

**200** – User ACL entry

Content-Type: `application/json`

```json
{
  "hasCustomAcl": true,
  "isSuperAdmin": true,
  "features": [
    "string"
  ],
  "organizations": null,
  "updatedAt": null
}
```

**400** – Invalid user id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/auth/users/acl?userId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/auth/users/acl`

Update user ACL

Configures per-user ACL overrides, including super admin access, feature list, and organization scope.

Requires features: auth.acl.manage

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.acl.manage

### Request Body

Content-Type: `application/json`

```json
{
  "userId": "00000000-0000-4000-8000-000000000000",
  "organizations": null
}
```

### Responses

**200** – User ACL updated

Content-Type: `application/json`

```json
{
  "ok": true,
  "sanitized": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/auth/users/acl" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"userId\": \"00000000-0000-4000-8000-000000000000\",
  \"organizations\": null
}"
```

## GET `/auth/users/consents`

List user consents

Returns all consent records for a given user, with integrity verification status.

Requires features: auth.users.edit

**Tags:** Auth

**Requires authentication.**

**Features:** auth.users.edit

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| userId | query | any | Required |

### Responses

**200** – Consent list returned

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/auth/users/consents?userId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/auth/users/resend-invite`

Resend invitation email

Resends the invitation email to a user who has not yet set up their password. Generates a new 48-hour setup token and invalidates prior tokens.

Requires features: auth.users.create

**Tags:** Authentication & Accounts

**Requires authentication.**

**Features:** auth.users.create

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Invite email sent

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid request origin

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – User not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – User already has a password

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Validation error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Invitation email origin is not configured

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/auth/users/resend-invite" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## DELETE `/better_auth/admin/reviewers`

Delete a store reviewer (admin)

Soft-deletes a store reviewer by id, revoking the fixed OTP.

Requires features: better_auth.reviewers.delete

**Tags:** Authentication

**Requires authentication.**

**Features:** better_auth.reviewers.delete

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. Store reviewer identifier |

### Responses

**200** – Reviewer deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Reviewer not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/better_auth/admin/reviewers?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/better_auth/admin/reviewers`

List store reviewers (admin)

Returns store-reviewer fixed-OTP accounts with pagination and search by email or label. When the query is a UUID it also matches the reviewer id.

Requires features: better_auth.reviewers.list

**Tags:** Authentication

**Requires authentication.**

**Features:** better_auth.reviewers.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| search | query | any | Optional |
| id | query | any | Optional |

### Responses

**200** – Paginated list of store reviewers

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "email": "string",
      "otpCode": "string",
      "label": null,
      "isActive": true,
      "createdAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/better_auth/admin/reviewers?page=1&pageSize=25&sortField=createdAt&sortDir=desc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/better_auth/admin/reviewers`

Create a store reviewer (admin)

Registers an email whose fixed 6-digit OTP always authenticates via the OTP sign-in flow. Used for App Store / Google Play reviewers. The code is generated server-side from a CSPRNG (never supplied by the caller) and is not echoed in this response — read it from the reviewers list. Rejects (409) an email that already belongs to a reviewer, or to a superadmin or any non-consumer user.

Requires features: better_auth.reviewers.create

**Tags:** Authentication

**Requires authentication.**

**Features:** better_auth.reviewers.create

### Request Body

Content-Type: `application/json`

```json
{
  "email": "user@example.com",
  "label": null
}
```

### Responses

**201** – Reviewer created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Email already exists or belongs to a privileged account

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/better_auth/admin/reviewers" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"email\": \"user@example.com\",
  \"label\": null
}"
```

## PUT `/better_auth/admin/reviewers`

Update a store reviewer (admin)

Updates a store reviewer by id (email, label, active). The fixed code cannot be set to a chosen value; pass regenerateOtpCode: true to draw a new one server-side. Rejects (409) an email that already belongs to another reviewer, or to a superadmin or any non-consumer user.

Requires features: better_auth.reviewers.update

**Tags:** Authentication

**Requires authentication.**

**Features:** better_auth.reviewers.update

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "label": null
}
```

### Responses

**200** – Reviewer updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Reviewer not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Email already exists or belongs to a privileged account

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/better_auth/admin/reviewers" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"label\": null
}"
```

## POST `/better_auth/otp/request`

Request a 6-digit email OTP for sign-in, sign-up or action confirmation

Sends a one-time code to the provided email address. The optional "purpose" field selects the email template: "sign-in" (default) sends the login email with a one-tap universal link, while "action-confirmation" sends confirmation copy with no login link — used by the in-app step-up verification sheet for risky actions. The code itself is currently identical in both cases (a confirmation code can still be used to sign in); only the email presentation differs. Always returns 200 with { ok: true } regardless of whether the email maps to an existing user, to prevent account enumeration. Rate limited by client IP and by IP + hashed email (fail-open, before any DB work); exceeding either returns 429 { error } with Retry-After and X-RateLimit-* headers. Store-reviewer allowlisted emails are rate limited as well. A per-email cooldown (60s, sha256-keyed) returns 429 { error: "otp_cooldown", retryAfterSeconds } with a Retry-After header — the cooldown is keyed independently of user existence, so it does not leak enumeration signal, and reviewer emails skip it. Plugin errors are logged but never surfaced. The OTP email is localized from the request locale (locale cookie, then Accept-Language header); when the request carries no locale signal, the sender falls back to the consumer profile language, then English.

**Tags:** Authentication

### Request Body

Content-Type: `application/json`

```json
{
  "email": "user@example.com",
  "purpose": "sign-in"
}
```

### Responses

**200** – Request accepted (no signal about account existence)

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – IP or IP + email rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/better_auth/otp/request" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"email\": \"user@example.com\",
  \"purpose\": \"sign-in\"
}"
```

## POST `/better_auth/otp/verify`

Verify a 6-digit email OTP and issue a platform session

Verifies the OTP via BetterAuth (atomic verification with attempt counter and expiry). On success, creates the user if missing, then issues a platform JWT and refresh token. Returns generic error messages for invalid/expired codes to avoid leaking which one applied. Rate limited (fail-open, before any DB work) by client IP and by IP + hashed email; store-reviewer emails carry an additional IP-independent cap because their code is static and never expires. Exceeding any of them returns 429 with Retry-After and X-RateLimit-* headers. A successful sign-in clears the per-email counters.

**Tags:** Authentication

### Request Body

Content-Type: `application/json`

```json
{
  "email": "user@example.com",
  "otp": "string"
}
```

### Responses

**200** – OTP verified, session issued

Content-Type: `application/json`

```json
{
  "token": "string",
  "refreshToken": "string",
  "user": {
    "id": "00000000-0000-4000-8000-000000000000",
    "userId": "00000000-0000-4000-8000-000000000000",
    "email": "user@example.com",
    "firstName": null,
    "lastName": null,
    "phone": null,
    "languageCode": null,
    "displayCurrency": null,
    "displayCurrencyChangedAt": null,
    "homeCountryCode": null,
    "travelProfile": null,
    "birthDate": null,
    "address": null,
    "billing": null,
    "active": true,
    "createdAt": "string"
  },
  "isNewUser": true
}
```

**400** – Invalid payload, invalid or expired code, or unsupported currency

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – IP, IP + email, or store-reviewer per-email rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – OTP login failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/better_auth/otp/verify" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"email\": \"user@example.com\",
  \"otp\": \"string\"
}"
```

## POST `/better_auth/social`

Sign in or sign up with a social provider

Verifies the provider ID token via BetterAuth, creates or finds the user, and returns a platform JWT.

**Tags:** Authentication

### Request Body

Content-Type: `application/json`

```json
{
  "provider": "google",
  "token": "string"
}
```

### Responses

**200** – SSO authentication successful

Content-Type: `application/json`

```json
{
  "token": "string",
  "refreshToken": "string",
  "user": {
    "id": "00000000-0000-4000-8000-000000000000",
    "userId": "00000000-0000-4000-8000-000000000000",
    "email": "user@example.com",
    "firstName": null,
    "lastName": null,
    "phone": null,
    "languageCode": null,
    "displayCurrency": null,
    "displayCurrencyChangedAt": null,
    "homeCountryCode": null,
    "travelProfile": null,
    "birthDate": null,
    "address": null,
    "billing": null,
    "active": true,
    "createdAt": "string"
  },
  "isNewUser": true
}
```

**400** – Invalid payload or unsupported provider

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Invalid or expired token

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – SSO processing failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/better_auth/social" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"provider\": \"google\",
  \"token\": \"string\"
}"
```

## POST `/business_rules/execute`

Execute rules for given context

Manually executes applicable business rules for the specified entity type, event, and data. Supports dry-run mode to test rules without executing actions.

Requires features: business_rules.execute

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.execute

### Request Body

Content-Type: `application/json`

```json
{
  "entityType": "string",
  "dryRun": false
}
```

### Responses

**200** – Rules executed successfully

Content-Type: `application/json`

```json
{
  "allowed": true,
  "executedRules": [
    {
      "ruleId": "string",
      "ruleName": "string",
      "conditionResult": true,
      "executionTime": 1
    }
  ],
  "totalExecutionTime": 1
}
```

**400** – Invalid request payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Execution error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/business_rules/execute" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityType\": \"string\",
  \"dryRun\": false
}"
```

## POST `/business_rules/execute/{ruleId}`

Execute a specific rule by its database UUID

Directly executes a specific business rule identified by its UUID, bypassing the normal entityType/eventType discovery mechanism. Useful for workflows and targeted rule execution.

Requires features: business_rules.execute

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.execute

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| ruleId | path | any | Required. The database UUID of the business rule to execute |

### Request Body

Content-Type: `application/json`

```json
{
  "dryRun": false
}
```

### Responses

**200** – Rule executed successfully

Content-Type: `application/json`

```json
{
  "success": true,
  "ruleId": "string",
  "ruleName": "string",
  "conditionResult": true,
  "actionsExecuted": null,
  "executionTime": 1
}
```

**400** – Invalid request payload or rule ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Rule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Execution error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/business_rules/execute/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"dryRun\": false
}"
```

## GET `/business_rules/logs`

List rule execution logs

Returns rule execution history for the current tenant and organization with filtering and pagination. Useful for audit trails and debugging.

Requires features: business_rules.view_logs

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.view_logs

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| ruleId | query | any | Optional |
| entityId | query | any | Optional |
| entityType | query | any | Optional |
| executionResult | query | any | Optional |
| executedBy | query | any | Optional |
| executedAtFrom | query | any | Optional |
| executedAtTo | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Rule execution logs collection

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "ruleId": "string",
      "ruleName": "string",
      "ruleType": "string",
      "entityId": "00000000-0000-4000-8000-000000000000",
      "entityType": "string",
      "executionResult": "SUCCESS",
      "inputContext": null,
      "outputContext": null,
      "errorMessage": null,
      "executionTimeMs": 1,
      "executedAt": "string",
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": null,
      "executedBy": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/business_rules/logs?page=1&pageSize=50&sortDir=desc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/business_rules/logs/{id}`

Get execution log detail

Returns detailed information about a specific rule execution, including full context and results.

Requires features: business_rules.view_logs

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.view_logs

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Log entry details

Content-Type: `application/json`

```json
{
  "id": "string",
  "rule": {
    "id": "00000000-0000-4000-8000-000000000000",
    "ruleId": "string",
    "ruleName": "string",
    "ruleType": "string",
    "entityType": "string"
  },
  "entityId": "00000000-0000-4000-8000-000000000000",
  "entityType": "string",
  "executionResult": "SUCCESS",
  "inputContext": null,
  "outputContext": null,
  "errorMessage": null,
  "executionTimeMs": 1,
  "executedAt": "string",
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": null,
  "executedBy": null
}
```

**400** – Invalid log id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Log entry not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/business_rules/logs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/business_rules/rules`

Delete business rule

Soft deletes a business rule by identifier.

Requires features: business_rules.manage

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. Business rule identifier |

### Responses

**200** – Business rule deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Business rule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/business_rules/rules?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/business_rules/rules`

List business rules

Returns business rules for the current tenant and organization with filtering and pagination.

Requires features: business_rules.view

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| ruleId | query | any | Optional |
| ruleType | query | any | Optional |
| entityType | query | any | Optional |
| eventType | query | any | Optional |
| enabled | query | any | Optional |
| ruleCategory | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Business rules collection

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "ruleId": "string",
      "ruleName": "string",
      "description": null,
      "ruleType": "GUARD",
      "ruleCategory": null,
      "entityType": "string",
      "eventType": null,
      "enabled": true,
      "priority": 1,
      "version": 1,
      "effectiveFrom": null,
      "effectiveTo": null,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/business_rules/rules?page=1&pageSize=50&sortDir=desc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/business_rules/rules`

Create business rule

Creates a new business rule for the current tenant and organization.

Requires features: business_rules.manage

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.manage

### Request Body

Content-Type: `application/json`

```json
{
  "ruleId": "string",
  "ruleName": "string",
  "description": null,
  "ruleType": "GUARD",
  "ruleCategory": null,
  "entityType": "string",
  "eventType": null,
  "enabled": true,
  "priority": 100,
  "version": 1,
  "effectiveFrom": null,
  "effectiveTo": null,
  "tenantId": "string",
  "organizationId": "string",
  "createdBy": null,
  "conditionExpression": null,
  "successActions": null,
  "failureActions": null
}
```

### Responses

**201** – Business rule created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/business_rules/rules" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"ruleId\": \"string\",
  \"ruleName\": \"string\",
  \"description\": null,
  \"ruleType\": \"GUARD\",
  \"ruleCategory\": null,
  \"entityType\": \"string\",
  \"eventType\": null,
  \"enabled\": true,
  \"priority\": 100,
  \"version\": 1,
  \"effectiveFrom\": null,
  \"effectiveTo\": null,
  \"tenantId\": \"string\",
  \"organizationId\": \"string\",
  \"createdBy\": null,
  \"conditionExpression\": null,
  \"successActions\": null,
  \"failureActions\": null
}"
```

## PUT `/business_rules/rules`

Update business rule

Updates an existing business rule.

Requires features: business_rules.manage

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.manage

### Request Body

Content-Type: `application/json`

```json
{
  "description": null,
  "ruleCategory": null,
  "eventType": null,
  "enabled": true,
  "priority": 100,
  "version": 1,
  "effectiveFrom": null,
  "effectiveTo": null,
  "conditionExpression": null,
  "successActions": null,
  "failureActions": null,
  "id": "string"
}
```

### Responses

**200** – Business rule updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Business rule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/business_rules/rules" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"description\": null,
  \"ruleCategory\": null,
  \"eventType\": null,
  \"enabled\": true,
  \"priority\": 100,
  \"version\": 1,
  \"effectiveFrom\": null,
  \"effectiveTo\": null,
  \"conditionExpression\": null,
  \"successActions\": null,
  \"failureActions\": null,
  \"id\": \"string\"
}"
```

## GET `/business_rules/rules/{id}`

Fetch business rule by ID

Returns complete details of a business rule including conditions and actions.

Requires features: business_rules.view

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Business rule detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "ruleId": "string",
  "ruleName": "string",
  "description": null,
  "ruleType": "GUARD",
  "ruleCategory": null,
  "entityType": "string",
  "eventType": null,
  "successActions": null,
  "failureActions": null,
  "enabled": true,
  "priority": 1,
  "version": 1,
  "effectiveFrom": null,
  "effectiveTo": null,
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "createdBy": null,
  "updatedBy": null,
  "createdAt": "string",
  "updatedAt": "string"
}
```

**400** – Invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Business rule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/business_rules/rules/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/business_rules/sets`

Delete rule set

Soft deletes a rule set by identifier.

Requires features: business_rules.manage_sets

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.manage_sets

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. Rule set identifier |

### Responses

**200** – Rule set deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Rule set not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/business_rules/sets?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/business_rules/sets`

List rule sets

Returns rule sets for the current tenant and organization with filtering and pagination.

Requires features: business_rules.view

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| setId | query | any | Optional |
| enabled | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – Rule sets collection

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "setId": "string",
      "setName": "string",
      "description": null,
      "enabled": true,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdBy": null,
      "updatedBy": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/business_rules/sets?page=1&pageSize=50&sortDir=asc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/business_rules/sets`

Create rule set

Creates a new rule set for organizing business rules.

Requires features: business_rules.manage_sets

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.manage_sets

### Request Body

Content-Type: `application/json`

```json
{
  "setId": "string",
  "setName": "string",
  "description": null,
  "enabled": true,
  "tenantId": "string",
  "organizationId": "string",
  "createdBy": null
}
```

### Responses

**201** – Rule set created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/business_rules/sets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"setId\": \"string\",
  \"setName\": \"string\",
  \"description\": null,
  \"enabled\": true,
  \"tenantId\": \"string\",
  \"organizationId\": \"string\",
  \"createdBy\": null
}"
```

## PUT `/business_rules/sets`

Update rule set

Updates an existing rule set.

Requires features: business_rules.manage_sets

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.manage_sets

### Request Body

Content-Type: `application/json`

```json
{
  "description": null,
  "enabled": true,
  "id": "string"
}
```

### Responses

**200** – Rule set updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Rule set not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/business_rules/sets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"description\": null,
  \"enabled\": true,
  \"id\": \"string\"
}"
```

## GET `/business_rules/sets/{id}`

Get rule set detail

Returns detailed information about a specific rule set, including all member rules.

Requires features: business_rules.view

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Rule set details

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "setId": "string",
  "setName": "string",
  "description": null,
  "enabled": true,
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "createdBy": null,
  "updatedBy": null,
  "createdAt": "string",
  "updatedAt": "string",
  "members": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "ruleId": "00000000-0000-4000-8000-000000000000",
      "ruleName": "string",
      "ruleType": "string",
      "sequence": 1,
      "enabled": true
    }
  ]
}
```

**400** – Invalid rule set id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Rule set not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/business_rules/sets/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/business_rules/sets/{id}/members`

Remove rule from set

Removes a business rule from a rule set (hard delete).

Requires features: business_rules.manage_sets

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.manage_sets

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| memberId | query | any | Required. Member identifier |

### Responses

**200** – Member removed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Member not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/business_rules/sets/:id/members?memberId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/business_rules/sets/{id}/members`

Add rule to set

Adds a business rule to a rule set with specified sequence and enabled state.

Requires features: business_rules.manage_sets

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.manage_sets

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "ruleId": "00000000-0000-4000-8000-000000000000",
  "sequence": 0,
  "enabled": true
}
```

### Responses

**201** – Member added

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Rule set or rule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Rule already in set

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/business_rules/sets/:id/members" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"ruleId\": \"00000000-0000-4000-8000-000000000000\",
  \"sequence\": 0,
  \"enabled\": true
}"
```

## PUT `/business_rules/sets/{id}/members`

Update set member

Updates sequence or enabled state of a rule set member.

Requires features: business_rules.manage_sets

**Tags:** Business Rules

**Requires authentication.**

**Features:** business_rules.manage_sets

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "memberId": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Member updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Member not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/business_rules/sets/:id/members" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"memberId\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/companies/admin`

List companies

Returns a paginated list of companies scoped to the caller organization/tenant (self plus descendants). A superadmin sees every company.

Requires features: companies.admin.list

**Tags:** Companies Admin

**Requires authentication.**

**Features:** companies.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – Company list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "vatId": null,
      "viesStatus": "unverified",
      "onboardingState": "created",
      "companySizeBand": null,
      "acquisitionSource": null,
      "createdAt": "string"
    }
  ],
  "total": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/companies/admin?limit=20&offset=0" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/companies/admin`

Create a company for an existing organization

Attaches a company satellite to an existing directory organization inside the caller scope. The organization itself is not created here. One active company per organization — a second one conflicts. Runs through the action-logged companies.company.create command.

Requires features: companies.admin.create

**Tags:** Companies Admin

**Requires authentication.**

**Features:** companies.admin.create

### Request Body

Content-Type: `application/json`

```json
{
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "vatId": null,
  "companySizeBand": null,
  "acquisitionSource": null
}
```

### Responses

**201** – Company created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "vatId": null,
  "viesStatus": "unverified",
  "onboardingState": "created",
  "companySizeBand": null,
  "acquisitionSource": null,
  "createdAt": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Organization not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – The organization already has a company

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/companies/admin" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"organizationId\": \"00000000-0000-4000-8000-000000000000\",
  \"vatId\": null,
  \"companySizeBand\": null,
  \"acquisitionSource\": null
}"
```

## GET `/companies/admin/{id}`

Get company detail

Returns a single company scoped to the caller organization/tenant. A company in another organization reads as not found.

Requires features: companies.admin.view

**Tags:** Companies Admin

**Requires authentication.**

**Features:** companies.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Company

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "vatId": null,
  "viesStatus": "unverified",
  "onboardingState": "created",
  "companySizeBand": null,
  "acquisitionSource": null,
  "createdAt": "string"
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Company not found (or outside caller organization)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/companies/admin/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/companies/admin/{id}`

Update a company

Updates any company inside the caller scope; a company in another organization reads as not found. Setting a new VAT ID resets viesStatus to 'unverified' unless viesStatus is passed explicitly (an ops override until the VIES checker owns it). Runs through the action-logged companies.company.update command.

Requires features: companies.admin.update

**Tags:** Companies Admin

**Requires authentication.**

**Features:** companies.admin.update

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "vatId": null
}
```

### Responses

**200** – Updated company

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "vatId": null,
  "viesStatus": "unverified",
  "onboardingState": "created",
  "companySizeBand": null,
  "acquisitionSource": null,
  "createdAt": "string"
}
```

**400** – Invalid payload or params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Company not found (or outside caller organization)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/companies/admin/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"vatId\": null
}"
```

## GET `/companies/me`

Get the authenticated user's company

Returns the company satellite attached to the caller's own organization (auth.orgId). A caller whose organization scope cannot be resolved gets 403; an organization without a company row gets 404.

Requires features: companies.view

**Tags:** Companies

**Requires authentication.**

**Features:** companies.view

### Responses

**200** – Company

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "vatId": null,
  "viesStatus": "unverified",
  "onboardingState": "created",
  "companySizeBand": null,
  "acquisitionSource": null,
  "createdAt": "string"
}
```

**404** – Company not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/companies/me" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/companies/me`

Update the authenticated user's company

Updates the caller's own company profile. Setting a new VAT ID resets viesStatus to 'unverified' — VIES is re-checked before every invoice is finalized. Runs through the action-logged companies.company.update command.

Requires features: companies.update

**Tags:** Companies

**Requires authentication.**

**Features:** companies.update

### Request Body

Content-Type: `application/json`

```json
{
  "vatId": null
}
```

### Responses

**200** – Updated company

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "vatId": null,
  "viesStatus": "unverified",
  "onboardingState": "created",
  "companySizeBand": null,
  "acquisitionSource": null,
  "createdAt": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Company not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/companies/me" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"vatId\": null
}"
```

## POST `/companies/signup`

Create an organization, its company and the owner account

Verifies a 6-digit email OTP obtained from POST /api/better-auth/otp/request, then creates a new organization in the shared tenant, its company satellite, and the signing-up owner inside that organization with the company_owner role. Returns a platform session scoped to the new organization. One organization per login: an email that already has a Covo account is rejected with 409 and nothing is created. A failed OTP removes the organization created for the attempt, so no orphan organization is left behind. No wallet is created — funding the wallet is the next onboarding step. Rate limited by client IP and by IP + email.

**Tags:** Companies

### Request Body

Content-Type: `application/json`

```json
{
  "email": "user@example.com",
  "otp": "string",
  "companyName": "string",
  "vatId": null,
  "companySizeBand": null,
  "acquisitionSource": null
}
```

### Responses

**201** – Company created and the owner signed in

Content-Type: `application/json`

```json
{
  "token": "string",
  "refreshToken": "string",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "company": {
    "id": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "vatId": null,
    "viesStatus": "unverified",
    "onboardingState": "created",
    "companySizeBand": null,
    "acquisitionSource": null,
    "createdAt": "string"
  }
}
```

**400** – Invalid payload, or invalid or expired code

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – The email already has a Covo account

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Too many attempts, or IP / IP + email rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Signup failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/companies/signup" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"email\": \"user@example.com\",
  \"otp\": \"string\",
  \"companyName\": \"string\",
  \"vatId\": null,
  \"companySizeBand\": null,
  \"acquisitionSource\": null
}"
```

## GET `/configs/cache`

Get cache statistics

Returns detailed cache statistics including total entries and breakdown by cache segments. Requires cache service to be available.

Requires features: configs.cache.view

**Tags:** Configs

**Requires authentication.**

**Features:** configs.cache.view

### Responses

**200** – Cache statistics

Content-Type: `application/json`

```json
{
  "generatedAt": "string",
  "totalKeys": 1,
  "segments": [
    {
      "segment": "string",
      "resource": null,
      "method": null,
      "path": null,
      "keyCount": 1,
      "keys": [
        "string"
      ]
    }
  ]
}
```

**500** – Failed to resolve cache stats

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Cache service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/configs/cache" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/configs/cache`

Purge cache

Purges cache entries. Supports two actions: purgeAll (clears entire cache) or purgeSegment (clears specific segment). Returns updated cache statistics after purge.

Requires features: configs.cache.manage

**Tags:** Configs

**Requires authentication.**

**Features:** configs.cache.manage

### Request Body

Content-Type: `application/json`

```json
{
  "action": "purgeAll"
}
```

### Responses

**200** – Cache segment cleared successfully

Content-Type: `application/json`

```json
{
  "action": "purgeSegment",
  "segment": "string",
  "deleted": 1,
  "stats": {
    "generatedAt": "string",
    "totalKeys": 1,
    "segments": [
      {
        "segment": "string",
        "resource": null,
        "method": null,
        "path": null,
        "keyCount": 1,
        "keys": [
          "string"
        ]
      }
    ]
  }
}
```

**400** – Invalid request - missing segment identifier for purgeSegment action

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to purge cache

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Cache service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/configs/cache" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"action\": \"purgeAll\"
}"
```

## DELETE `/configs/module-telemetry`

Clear module telemetry data

Development-only endpoint that clears in-memory module telemetry and local process telemetry files.

Requires features: configs.manage

**Tags:** Configs

**Requires authentication.**

**Features:** configs.manage

### Responses

**200** – Module telemetry cleared

Content-Type: `application/json`

```json
{
  "cleared": true
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/configs/module-telemetry" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/configs/module-telemetry`

Get module resource usage telemetry

Returns in-process module resource attribution for API routes, event subscribers, and queue workers.

Requires features: configs.system_status.view

**Tags:** Configs

**Requires authentication.**

**Features:** configs.system_status.view

### Responses

**200** – Module resource usage report

Content-Type: `application/json`

```json
{
  "generatedAt": "string",
  "startedAt": "string",
  "enabled": true,
  "bucketIntervalMs": 1,
  "totals": {
    "modules": 1,
    "operations": 1,
    "calls": 1,
    "errors": 1,
    "totalDurationMs": 1,
    "totalCpuMs": 1,
    "positiveHeapDeltaBytes": 1,
    "positiveRssDeltaBytes": 1
  },
  "thresholds": {
    "p95DurationMs": 1,
    "cpuMs": 1,
    "positiveHeapDeltaBytes": 1,
    "positiveRssDeltaBytes": 1,
    "errors": 1
  },
  "modules": [
    {
      "moduleId": "string",
      "calls": 1,
      "errors": 1,
      "totalDurationMs": 1,
      "p95DurationMs": 1,
      "totalCpuMs": 1,
      "positiveHeapDeltaBytes": 1,
      "positiveRssDeltaBytes": 1,
      "surfaces": [
        {
          "surface": "api",
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuMs": 1,
          "positiveHeapDeltaBytes": 1,
          "positiveRssDeltaBytes": 1
        }
      ],
      "topOperations": [
        {
          "moduleId": "string",
          "surface": "api",
          "operation": "string",
          "resourceId": null,
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "maxDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuUserMs": 1,
          "totalCpuSystemMs": 1,
          "maxCpuMs": 1,
          "totalHeapDeltaBytes": 1,
          "positiveHeapDeltaBytes": 1,
          "maxHeapDeltaBytes": 1,
          "totalRssDeltaBytes": 1,
          "positiveRssDeltaBytes": 1,
          "maxRssDeltaBytes": 1,
          "firstSeenAt": "string",
          "lastSeenAt": "string"
        }
      ],
      "candidateReasons": [
        "string"
      ]
    }
  ],
  "candidates": [
    {
      "moduleId": "string",
      "calls": 1,
      "errors": 1,
      "totalDurationMs": 1,
      "p95DurationMs": 1,
      "totalCpuMs": 1,
      "positiveHeapDeltaBytes": 1,
      "positiveRssDeltaBytes": 1,
      "surfaces": [
        {
          "surface": "api",
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuMs": 1,
          "positiveHeapDeltaBytes": 1,
          "positiveRssDeltaBytes": 1
        }
      ],
      "topOperations": [
        {
          "moduleId": "string",
          "surface": "api",
          "operation": "string",
          "resourceId": null,
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "maxDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuUserMs": 1,
          "totalCpuSystemMs": 1,
          "maxCpuMs": 1,
          "totalHeapDeltaBytes": 1,
          "positiveHeapDeltaBytes": 1,
          "maxHeapDeltaBytes": 1,
          "totalRssDeltaBytes": 1,
          "positiveRssDeltaBytes": 1,
          "maxRssDeltaBytes": 1,
          "firstSeenAt": "string",
          "lastSeenAt": "string"
        }
      ],
      "candidateReasons": [
        "string"
      ]
    }
  ],
  "buckets": [
    {
      "bucketStart": "string",
      "bucketEnd": "string",
      "bucketIntervalMs": 1,
      "stage": "startup",
      "partial": true,
      "totals": {
        "modules": 1,
        "calls": 1,
        "errors": 1,
        "totalDurationMs": 1,
        "totalCpuMs": 1,
        "positiveHeapDeltaBytes": 1,
        "positiveRssDeltaBytes": 1
      },
      "modules": [
        {
          "moduleId": "string",
          "calls": 1,
          "errors": 1,
          "totalDurationMs": 1,
          "p95DurationMs": 1,
          "totalCpuMs": 1,
          "positiveHeapDeltaBytes": 1,
          "positiveRssDeltaBytes": 1,
          "surfaces": [
            {
              "surface": "api",
              "calls": 1,
              "errors": 1,
              "totalDurationMs": 1,
              "p95DurationMs": 1,
              "totalCpuMs": 1,
              "positiveHeapDeltaBytes": 1,
              "positiveRssDeltaBytes": 1
            }
          ],
          "topOperations": [
            {
              "moduleId": "string",
              "surface": "api",
              "operation": "string",
              "resourceId": null,
              "calls": 1,
              "errors": 1,
              "totalDurationMs": 1,
              "maxDurationMs": 1,
              "p95DurationMs": 1,
              "totalCpuUserMs": 1,
              "totalCpuSystemMs": 1,
              "maxCpuMs": 1,
              "totalHeapDeltaBytes": 1,
              "positiveHeapDeltaBytes": 1,
              "maxHeapDeltaBytes": 1,
              "totalRssDeltaBytes": 1,
              "positiveRssDeltaBytes": 1,
              "maxRssDeltaBytes": 1,
              "firstSeenAt": "string",
              "lastSeenAt": "string"
            }
          ],
          "candidateReasons": [
            "string"
          ]
        }
      ]
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/configs/module-telemetry" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/configs/system-status`

Get system health status

Returns comprehensive system health information including environment details, version, resource usage, and service connectivity status.

Requires features: configs.system_status.view

**Tags:** Configs

**Requires authentication.**

**Features:** configs.system_status.view

### Responses

**200** – System status snapshot

Content-Type: `application/json`

```json
{
  "generatedAt": "string",
  "runtimeMode": "development",
  "categories": [
    {
      "key": "profiling",
      "labelKey": "string",
      "descriptionKey": null,
      "items": [
        {
          "key": "string",
          "category": "profiling",
          "kind": "boolean",
          "labelKey": "string",
          "descriptionKey": "string",
          "docUrl": null,
          "defaultValue": null,
          "state": "enabled",
          "value": null,
          "normalizedValue": null
        }
      ]
    }
  ]
}
```

**500** – Failed to load system status

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/configs/system-status" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/configs/system-status`

Clear system cache

Purges the entire cache for the current tenant. Useful for troubleshooting or forcing fresh data loading.

Requires features: configs.manage

**Tags:** Configs

**Requires authentication.**

**Features:** configs.manage

### Responses

**200** – Cache cleared successfully

Content-Type: `application/json`

```json
{
  "cleared": true
}
```

**500** – Failed to purge cache

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Cache service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/configs/system-status" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/configs/upgrade-actions`

List pending upgrade actions

Returns a list of pending upgrade actions for the current version. These are one-time setup tasks that need to be executed after upgrading to a new version. Requires organization and tenant context.

Requires features: configs.manage

**Tags:** Configs

**Requires authentication.**

**Features:** configs.manage

### Responses

**200** – List of pending upgrade actions

Content-Type: `application/json`

```json
{
  "version": "string",
  "actions": [
    {
      "id": "string",
      "version": "string",
      "message": "string",
      "ctaLabel": "string",
      "successMessage": "string",
      "loadingLabel": "string"
    }
  ]
}
```

**400** – Missing organization or tenant context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load upgrade actions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/configs/upgrade-actions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/configs/upgrade-actions`

Execute upgrade action

Executes a specific upgrade action by ID. Typically used for one-time setup tasks like seeding example data after version upgrade. Returns execution status and localized success message.

Requires features: configs.manage

**Tags:** Configs

**Requires authentication.**

**Features:** configs.manage

### Request Body

Content-Type: `application/json`

```json
{
  "actionId": "string"
}
```

### Responses

**200** – Upgrade action executed successfully

Content-Type: `application/json`

```json
{
  "status": "string",
  "message": "string",
  "version": "string"
}
```

**400** – Invalid request body or missing context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to execute upgrade action

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/configs/upgrade-actions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"actionId\": \"string\"
}"
```

## DELETE `/consumers/admin`

Delete consumer

Soft-deletes a consumer profile by id.

Requires features: consumers.admin.delete

**Tags:** Consumers

**Requires authentication.**

**Features:** consumers.admin.delete

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. Consumer profile identifier |

### Responses

**200** – Consumer deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Consumer not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/consumers/admin?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/consumers/admin`

List consumers

Returns consumers with pagination and search. The search query matches partial email (via the search token index), first name, and last name. When the query is a UUID it also matches the consumer id and user id. Pass id query param to get a single consumer. Items carry the admin-only flags hyperswitchOverride, paymentPlatformEnvironmentOverride, internalTestTopUpLimitExempt and esimBanned, which are absent from the consumer-facing /me response.

Requires features: consumers.admin.list

**Tags:** Consumers

**Requires authentication.**

**Features:** consumers.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| search | query | any | Optional |
| id | query | any | Optional |
| userId | query | any | Optional |

### Responses

**200** – Consumer list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "userId": "00000000-0000-4000-8000-000000000000",
      "email": "user@example.com",
      "firstName": null,
      "lastName": null,
      "phone": null,
      "languageCode": null,
      "displayCurrency": null,
      "displayCurrencyChangedAt": null,
      "homeCountryCode": null,
      "travelProfile": null,
      "birthDate": null,
      "address": null,
      "billing": null,
      "active": true,
      "createdAt": "string",
      "hyperswitchOverride": null,
      "paymentPlatformEnvironmentOverride": null,
      "internalTestTopUpLimitExempt": null,
      "esimBanned": true
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/consumers/admin?page=1&pageSize=50&sortField=createdAt&sortDir=desc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/consumers/admin`

Create consumer

Creates a new user and consumer profile.

Requires features: consumers.admin.create

**Tags:** Consumers

**Requires authentication.**

**Features:** consumers.admin.create

### Request Body

Content-Type: `application/json`

```json
{
  "email": "user@example.com",
  "password": "string",
  "firstName": null,
  "lastName": null,
  "phone": null,
  "languageCode": null,
  "displayCurrency": null,
  "homeCountryCode": null,
  "travelProfile": null,
  "birthDate": null,
  "address": null,
  "billing": null
}
```

### Responses

**201** – Consumer created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Email already exists

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/consumers/admin" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"email\": \"user@example.com\",
  \"password\": \"string\",
  \"firstName\": null,
  \"lastName\": null,
  \"phone\": null,
  \"languageCode\": null,
  \"displayCurrency\": null,
  \"homeCountryCode\": null,
  \"travelProfile\": null,
  \"birthDate\": null,
  \"address\": null,
  \"billing\": null
}"
```

## PUT `/consumers/admin`

Update consumer

Updates a consumer profile by id. Omitted fields are left untouched. internalTestTopUpLimitExempt is the admin-only per-consumer exemption from the internal-testing lifetime top-up cap (payments.internal_test_top_up_limit_eur_cents): only an explicit true lifts the cap for that consumer, null/false keep it enforced. paymentPlatformEnvironmentOverride pins the consumer to the sandbox or production merchant account regardless of the platform-wide toggle; null follows the toggle. Switching it makes the consumer mint a fresh payment-platform customer, so their previously saved cards and mandates become unreachable. esimBanned bans the consumer from provisioning any new eSIM (manual, admin or auto); already-provisioned eSIMs are untouched.

Requires features: consumers.admin.update

**Tags:** Consumers

**Requires authentication.**

**Features:** consumers.admin.update

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "firstName": null,
  "lastName": null,
  "phone": null,
  "languageCode": null,
  "displayCurrency": null,
  "homeCountryCode": null,
  "travelProfile": null,
  "birthDate": null,
  "address": null,
  "billing": null,
  "hyperswitchOverride": null,
  "paymentPlatformEnvironmentOverride": null,
  "internalTestTopUpLimitExempt": null
}
```

### Responses

**200** – Consumer updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Consumer not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/consumers/admin" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"firstName\": null,
  \"lastName\": null,
  \"phone\": null,
  \"languageCode\": null,
  \"displayCurrency\": null,
  \"homeCountryCode\": null,
  \"travelProfile\": null,
  \"birthDate\": null,
  \"address\": null,
  \"billing\": null,
  \"hyperswitchOverride\": null,
  \"paymentPlatformEnvironmentOverride\": null,
  \"internalTestTopUpLimitExempt\": null
}"
```

## GET `/consumers/admin/{id}/auto-refill`

Get consumer auto-refill configuration and recent charge

Returns auto-refill configuration, monthly spent counter, and the most recent auto-refill payment for the given consumer profile. Canonical EUR fields are always present, including the picker bounds amountRangeEuroCents (€5–€500) and thresholdRangeEuroCents (€0–€200). For non-EUR consumers the response also includes display-currency variants (displayAmount, displayThreshold, displayMonthlyCap, displayMonthlySpent, displayAmountRange, displayThresholdRange) in the consumer's display currency, omitted for EUR consumers and on any FX failure.

Requires features: consumers.admin.list

**Tags:** Consumers

**Requires authentication.**

**Features:** consumers.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Auto-refill admin detail

Content-Type: `application/json`

```json
{
  "enabled": true,
  "amountEuroCents": null,
  "thresholdEuroCents": null,
  "monthlyCapEuroCents": null,
  "monthlySpentEuroCents": 1,
  "pausedAt": null,
  "pausedReason": null,
  "lastCharge": null,
  "amountRangeEuroCents": {
    "minEuroCents": 1,
    "maxEuroCents": 1
  },
  "thresholdRangeEuroCents": {
    "minEuroCents": 1,
    "maxEuroCents": 1
  }
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Consumer not found or out of scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load auto-refill data

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/consumers/admin/00000000-0000-4000-8000-000000000000/auto-refill" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/consumers/admin/{id}/currency`

Set a consumer display currency (admin)

Backoffice admin changes the consumer display currency, repricing the wallet in one operation (the existing migrate-currency flow). Unlike the self-service change, this is exempt from the 7-day change-currency cooldown and the confirmation gate. A no-op (migrated=false) when the consumer is already on the target currency. Returns 400 for an unsupported currency and 422 when the consumer has no tenant/organization scope to reprice against.

Requires features: consumers.admin.update

**Tags:** Consumers

**Requires authentication.**

**Features:** consumers.admin.update

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "currency": "string"
}
```

### Responses

**200** – Display currency updated

Content-Type: `application/json`

```json
{
  "userId": "00000000-0000-4000-8000-000000000000",
  "displayCurrency": null,
  "migrated": true
}
```

**400** – Invalid body/params or unsupported currency

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Consumer not found or out of scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Consumer has no scope to reprice against

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/consumers/admin/00000000-0000-4000-8000-000000000000/currency" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"currency\": \"string\"
}"
```

## GET `/consumers/admin/{id}/spending-cap`

Get personal spending cap status for a consumer (admin)

Returns the personal cap, current monthly eSIM spend, and family membership flags (isFamilyMember/isFamilyAdmin) for the consumer. Always includes the canonical EUR cap range (capRangeEuroCents). When the consumer has a non-EUR display currency it also returns displayCap, displayMonthlySpent, and the localized displayCapRange (in the consumer's currency); these are omitted for EUR consumers and on FX failure.

Requires features: consumers.admin.view

**Tags:** Consumers

**Requires authentication.**

**Features:** consumers.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Personal spending cap status

Content-Type: `application/json`

```json
{
  "userId": "00000000-0000-4000-8000-000000000000",
  "personalSpendingCapEuroCents": null,
  "monthlySpentEuroCents": 1,
  "isFamilyMember": true,
  "isFamilyAdmin": true,
  "capRangeEuroCents": {
    "minEuroCents": 1,
    "maxEuroCents": 1
  }
}
```

**404** – Consumer not found or out of scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/consumers/admin/00000000-0000-4000-8000-000000000000/spending-cap" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/consumers/admin/{id}/spending-cap`

Set or clear personal monthly spending cap for a consumer (admin)

Backoffice admin sets or clears the monthly eSIM spending cap for the given consumer profile. The cap is entered in the consumer's display currency ({ cap: { currency, amountMinorUnits } }); the backend derives the canonical EUR value at set time and freezes the entered amount so it always reads back verbatim. Pass cap: null to clear. Rejected (400) if the consumer is a non-admin family member, and (422) if the currency cannot be converted.

Requires features: consumers.admin.update

**Tags:** Consumers

**Requires authentication.**

**Features:** consumers.admin.update

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "cap": null
}
```

### Responses

**200** – Personal spending cap updated

Content-Type: `application/json`

```json
{
  "userId": "00000000-0000-4000-8000-000000000000",
  "personalSpendingCapEuroCents": null
}
```

**400** – Invalid body, params, spending cap outside the allowed range, or consumer is a non-admin family member

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Consumer not found or out of scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Cap currency could not be converted to EUR

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/consumers/admin/00000000-0000-4000-8000-000000000000/spending-cap" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"cap\": null
}"
```

## GET `/consumers/admin/currency-usage`

Count consumers using a given display currency (admin)

Returns the number of active consumer profiles whose display currency matches the given ISO 4217 code, scoped to the caller tenant/organization. Used to warn admins before deactivating or deleting a currency, since affected consumers are moved to EUR.

Requires features: currencies.manage

**Tags:** Consumers

**Requires authentication.**

**Features:** currencies.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| code | query | any | Required |

### Responses

**200** – Consumer currency usage count

Content-Type: `application/json`

```json
{
  "code": "string",
  "count": 1
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load currency usage

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/consumers/admin/currency-usage?code=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/consumers/admin/remove`

Permanently remove consumer

Admin-initiated GDPR removal of a consumer (irreversible). Reverts pending outgoing gifts, cleans up family memberships, disables wallet auto-refill, redacts PII on the User and ConsumerProfile, revokes all sessions and detaches OAuth/password accounts, hard-deletes push devices and notification preferences, terminates active 1Global eSIM subscriptions and detaches Stripe payment methods, and writes an AccountDeletionWaiver retained for the financial record-retention period. Distinct from the reversible soft-delete on DELETE /consumers/admin.

Requires features: consumers.admin.remove

**Tags:** Consumers

**Requires authentication.**

**Features:** consumers.admin.remove

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Consumer permanently removed

Content-Type: `application/json`

```json
{
  "ok": true,
  "profileId": "00000000-0000-4000-8000-000000000000",
  "deletedAt": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Consumer not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Removal failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/consumers/admin/remove" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/consumers/intercom/token`

Issue an Intercom Identity Verification JWT

Returns a short-lived HS256 JWT signed with the Intercom Identity Verification secret. The mobile app passes this token to the Intercom SDK alongside the user id so Intercom can verify the session is legitimate.

**Tags:** Consumers

**Requires authentication.**

### Responses

**200** – Signed token

Content-Type: `application/json`

```json
{
  "token": "string",
  "userId": "00000000-0000-4000-8000-000000000000",
  "expiresAt": "string"
}
```

**404** – User not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Intercom identity verification not configured

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/consumers/intercom/token" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/consumers/me`

Delete current consumer account

Self-service account deletion (GDPR Art. 17 / Apple 5.1.1(v)). Requires recent reauthentication via email OTP or social ID token. Reverts pending outgoing gifts, cleans up family memberships, disables wallet auto-refill, redacts PII on User and ConsumerProfile, hard-deletes push devices and notification preferences, terminates active 1Global eSIM subscriptions and detaches Stripe payment methods, and writes an AccountDeletionWaiver record retained for the financial record-retention period.

**Tags:** Consumers

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "confirm": true,
  "waiveBalance": true,
  "waiverTextVersion": "string",
  "tosVersion": "string",
  "privacyPolicyVersion": "string",
  "reauth": {
    "method": "otp",
    "code": "string"
  }
}
```

### Responses

**200** – Account deleted

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "userId": "00000000-0000-4000-8000-000000000000",
  "deletedAt": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Reauthentication failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Consumer or user not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Deletion failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/consumers/me" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"confirm\": true,
  \"waiveBalance\": true,
  \"waiverTextVersion\": \"string\",
  \"tosVersion\": \"string\",
  \"privacyPolicyVersion\": \"string\",
  \"reauth\": {
    \"method\": \"otp\",
    \"code\": \"string\"
  }
}"
```

## GET `/consumers/me`

Get current consumer profile

Returns the authenticated consumer profile with Covo-specific fields.

**Tags:** Consumers

**Requires authentication.**

### Responses

**200** – Consumer profile

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "userId": "00000000-0000-4000-8000-000000000000",
  "email": "user@example.com",
  "firstName": null,
  "lastName": null,
  "phone": null,
  "languageCode": null,
  "displayCurrency": null,
  "displayCurrencyChangedAt": null,
  "homeCountryCode": null,
  "travelProfile": null,
  "birthDate": null,
  "address": null,
  "billing": null,
  "active": true,
  "createdAt": "string"
}
```

**404** – User not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/consumers/me" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/consumers/me`

Update consumer profile

Updates profile fields for the authenticated consumer. All fields are optional. Changing displayCurrency to a new value requires confirmCurrencyChange=true (the client first calls GET /api/me/currency-change-preview to show the converted amounts), otherwise the request is rejected with 400 and { requiresConfirmation: true }. A successful currency change is then hard-blocked for 7 days; an attempt within the cooldown is rejected with 409 and { cooldownUntil }. The response carries displayCurrencyChangedAt (the instant of the last change, or null).

**Tags:** Consumers

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "firstName": null,
  "lastName": null,
  "phone": null,
  "languageCode": null,
  "displayCurrency": null,
  "homeCountryCode": null,
  "travelProfile": null,
  "birthDate": null,
  "address": null,
  "billing": null
}
```

### Responses

**200** – Updated profile

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "userId": "00000000-0000-4000-8000-000000000000",
  "email": "user@example.com",
  "firstName": null,
  "lastName": null,
  "phone": null,
  "languageCode": null,
  "displayCurrency": null,
  "displayCurrencyChangedAt": null,
  "homeCountryCode": null,
  "travelProfile": null,
  "birthDate": null,
  "address": null,
  "billing": null,
  "active": true,
  "createdAt": "string"
}
```

**400** – Currency change requires confirmation

Content-Type: `application/json`

```json
{
  "error": "string",
  "requiresConfirmation": true
}
```

**404** – User not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Currency change is within the cooldown window

Content-Type: `application/json`

```json
{
  "error": "string",
  "cooldownUntil": null
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/consumers/me" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"firstName\": null,
  \"lastName\": null,
  \"phone\": null,
  \"languageCode\": null,
  \"displayCurrency\": null,
  \"homeCountryCode\": null,
  \"travelProfile\": null,
  \"birthDate\": null,
  \"address\": null,
  \"billing\": null
}"
```

## GET `/consumers/me/country-by-ip`

Resolve the caller's country from their request IP

Returns the ISO 3166-1 alpha-2 country code inferred from the request IP, or null when it cannot be determined (private/loopback IP, lookup miss, provider failure). Best-effort: this endpoint never fails the call due to lookup errors. The mobile app should treat null as 'unknown' rather than retrying aggressively.

**Tags:** Consumers

**Requires authentication.**

### Responses

**200** – Country code (or null if unresolved)

Content-Type: `application/json`

```json
{
  "countryCode": null,
  "ip": null
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/consumers/me/country-by-ip" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/consumers/me/currencies`

List the display currencies a user may select

Returns the currencies enabled for the tenant (Currency rows with isActive=true), i.e. the set a user may pick as their display currency. Restricted for launch via the fx.enabled_currency_codes feature toggle. EUR is always included. The mobile currency picker should render only these; a user already on a currency absent from this list keeps it (grandfathered) but is charged in EUR.

**Tags:** Consumers

**Requires authentication.**

### Responses

**200** – Active currencies

Content-Type: `application/json`

```json
{
  "currencies": [
    {
      "code": "string",
      "name": "string",
      "symbol": "string",
      "decimalPlaces": 1
    }
  ]
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/consumers/me/currencies" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/consumers/me/currency-change-preview`

Preview a display-currency change

Returns what the authenticated consumer will see after switching their display currency to the `currency` query param, without changing anything. Includes the standard, bonus and combined wallet balances and the saved auto-refill amount converted to the target currency, plus the 7-day change-currency cooldown status (canChange, cooldownUntil). The preview block is null for an EUR target (nothing converts) and on any FX failure — the endpoint never fails the request on a conversion error.

Requires features: wallet.balance.view

**Tags:** Consumers

**Requires authentication.**

**Features:** wallet.balance.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| currency | query | any | Required |

### Responses

**200** – Currency change preview

Content-Type: `application/json`

```json
{
  "currency": "string",
  "canChange": true,
  "cooldownUntil": null,
  "preview": null
}
```

**400** – Missing/invalid or unsupported currency

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load preview

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/consumers/me/currency-change-preview?currency=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/consumers/me/spending-cap`

Get the authenticated user personal monthly spending cap

Returns the personal cap (in EUR cents), current monthly eSIM spend, and family membership flags (isFamilyMember/isFamilyAdmin) for the authenticated consumer. Always includes the canonical EUR cap range (capRangeEuroCents). Non-EUR users additionally get display-currency variants: displayCap (the exact amount the user entered, frozen at set time and read back verbatim — never re-converted), displayMonthlySpent (live, converted + smart-rounded), and displayCapRange (the localized, hysteresis-dampened slider bounds). All display fields are omitted for EUR users and on FX failure.

**Tags:** Consumers

**Requires authentication.**

### Responses

**200** – Personal spending cap status

Content-Type: `application/json`

```json
{
  "userId": "00000000-0000-4000-8000-000000000000",
  "personalSpendingCapEuroCents": null,
  "monthlySpentEuroCents": 1,
  "isFamilyMember": true,
  "isFamilyAdmin": true,
  "capRangeEuroCents": {
    "minEuroCents": 1,
    "maxEuroCents": 1
  }
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/consumers/me/spending-cap" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/consumers/me/spending-cap`

Set or clear the authenticated user personal monthly spending cap

Sets the monthly eSIM spending cap for the calling user. The cap is entered in the user's display currency ({ cap: { currency, amountMinorUnits } }); the backend derives the canonical EUR value at set time and freezes the entered amount so it always reads back verbatim. Pass cap: null to clear. Rejected (400) if the user is a non-admin family member, and (422) if the currency cannot be converted.

**Tags:** Consumers

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "cap": null
}
```

### Responses

**200** – Personal spending cap updated

Content-Type: `application/json`

```json
{
  "userId": "00000000-0000-4000-8000-000000000000",
  "personalSpendingCapEuroCents": null
}
```

**400** – Invalid body, spending cap outside the allowed range, or user is a non-admin family member

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Consumer profile not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Cap currency could not be converted to EUR

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/consumers/me/spending-cap" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"cap\": null
}"
```

## DELETE `/countries/admin/countries`

Delete country

Soft-deletes a country by ISO-2 code (passed as `id` query param). Blocks deletion when the code is still referenced by any eSIM, usage snapshot, or consumer profile. Action is logged and reversible via undo.

Requires features: countries.admin.delete

**Tags:** Countries

**Requires authentication.**

**Features:** countries.admin.delete

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required |

### Responses

**200** – Country deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Country not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Country in use by other records

Content-Type: `application/json`

```json
{
  "error": "string",
  "references": {
    "esims": 1,
    "usageSnapshots": 1,
    "consumerProfiles": 1
  }
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/countries/admin/countries?id=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/countries/admin/countries`

List countries (admin)

Returns the country pricing catalog with pagination, search by code or name, and an optional blocked/allowed filter. Items include blocked-country metadata.

Requires features: countries.admin.list

**Tags:** Countries

**Requires authentication.**

**Features:** countries.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| search | query | any | Optional |
| blocked | query | any | Optional |

### Responses

**200** – Country list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "code": "string",
      "name": "string",
      "flagUrl": null,
      "zone": "A",
      "pricingRating": 1,
      "pricePerGb": {
        "amountInMinorUnits": 1,
        "currency": "string"
      },
      "canRoam": true,
      "canSell": true,
      "canUseInOneGlobal": true,
      "isBlocked": true,
      "blockedSeverity": null,
      "appStoreBlocked": true,
      "apiBlocked": true,
      "smdpBlocked": true,
      "vpnAdvice": true,
      "requirePreInstall": true,
      "blockedMessage": null,
      "localizedBlockedMessages": null,
      "hasProductOfferings": true
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/countries/admin/countries?page=1&pageSize=50&sortField=code&sortDir=asc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/countries/admin/countries`

Create country

Creates a new country in the catalog with pricing, roaming/sell flags, and optional blocked-country metadata. Action is logged and reversible via undo (soft-deletes the new row).

Requires features: countries.admin.create

**Tags:** Countries

**Requires authentication.**

**Features:** countries.admin.create

### Request Body

Content-Type: `application/json`

```json
{
  "code": "string",
  "name": "string",
  "zone": "A",
  "pricingRating": 1,
  "pricePerGb": {
    "amountInMinorUnits": 1,
    "currency": "EUR"
  },
  "flagUrl": null,
  "canRoam": true,
  "canSell": true,
  "canUseInOneGlobal": false,
  "blockedSeverity": null,
  "blockedMessage": null,
  "localizedBlockedMessages": null
}
```

### Responses

**201** – Country created

Content-Type: `application/json`

```json
{
  "ok": true,
  "code": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Country already exists

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/countries/admin/countries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"code\": \"string\",
  \"name\": \"string\",
  \"zone\": \"A\",
  \"pricingRating\": 1,
  \"pricePerGb\": {
    \"amountInMinorUnits\": 1,
    \"currency\": \"EUR\"
  },
  \"flagUrl\": null,
  \"canRoam\": true,
  \"canSell\": true,
  \"canUseInOneGlobal\": false,
  \"blockedSeverity\": null,
  \"blockedMessage\": null,
  \"localizedBlockedMessages\": null
}"
```

## PUT `/countries/admin/countries`

Update country pricing

Updates the per-GB price, expensiveness rating (1–5), roaming/sell flags, or blocked-country metadata for a country. Identified by ISO-2 code in the body `id` field. Action is logged and reversible via undo.

Requires features: countries.admin.update

**Tags:** Countries

**Requires authentication.**

**Features:** countries.admin.update

### Request Body

Content-Type: `application/json`

```json
{
  "id": "string",
  "blockedSeverity": null,
  "blockedMessage": null,
  "localizedBlockedMessages": null
}
```

### Responses

**200** – Country updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Country not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/countries/admin/countries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\",
  \"blockedSeverity\": null,
  \"blockedMessage\": null,
  \"localizedBlockedMessages\": null
}"
```

## GET `/countries/blocked`

List blocked countries

Returns countries with known eSIM-related restrictions (blocked apps/APIs/SM-DP+) and pre-travel enforcement metadata. The restriction `message` is localised to the request locale (Accept-Language header, falling back to English). Unauthenticated; cached for 24 hours and varies by Accept-Language. Read by the mobile app on launch.

**Tags:** Countries

### Responses

**200** – Blocked country list

Content-Type: `application/json`

```json
{
  "countries": [
    {
      "countryCode": "string",
      "name": "string",
      "severity": "full",
      "restrictions": {
        "appStoreBlocked": true,
        "apiBlocked": true,
        "smdpBlocked": true
      },
      "vpnAdvice": true,
      "requirePreInstall": true,
      "message": "string"
    }
  ]
}
```

**500** – Failed to load blocked countries

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/countries/blocked" \
  -H "Accept: application/json"
```

## GET `/countries/countries`

List countries

Returns the catalog of supported countries with per-GB pricing, expensiveness rating (1–5), zone, and roaming/sell flags. Country names are localized to the caller's stored language (ConsumerProfile.languageCode), falling back to English. Supports pagination and search by code, English name, or the localized name in the caller's language (diacritic-insensitive).

Requires features: countries.list

**Tags:** Countries

**Requires authentication.**

**Features:** countries.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| search | query | any | Optional |

### Responses

**200** – Country list

Content-Type: `application/json`

```json
{
  "countries": [
    {
      "code": "string",
      "name": "string",
      "flagUrl": null,
      "zone": "A",
      "pricingRating": 1,
      "pricePerGb": {
        "amountInMinorUnits": 1,
        "currency": "string"
      },
      "canRoam": true,
      "canSell": true,
      "canUseInOneGlobal": true
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/countries/countries?page=1&pageSize=50&sortField=code&sortDir=asc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/currencies/currencies`

Delete currency

Deletes a currency by id.

Requires features: currencies.manage

**Tags:** Currencies

**Requires authentication.**

**Features:** currencies.manage

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Currency deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/currencies/currencies" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/currencies/currencies`

List currencies

Returns a paginated collection of currencies scoped to the authenticated organization.

Requires features: currencies.view

**Tags:** Currencies

**Requires authentication.**

**Features:** currencies.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| isBase | query | any | Optional |
| isActive | query | any | Optional |
| code | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated currencies

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "code": "string",
      "name": "string",
      "symbol": null,
      "decimalPlaces": 1,
      "thousandsSeparator": null,
      "decimalSeparator": null,
      "isBase": true,
      "isActive": true,
      "createdAt": null,
      "updatedAt": null,
      "organizationId": "string",
      "tenantId": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/currencies/currencies?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/currencies/currencies`

Create currency

Creates a new currency.

Requires features: currencies.manage

**Tags:** Currencies

**Requires authentication.**

**Features:** currencies.manage

### Request Body

Content-Type: `application/json`

```json
{
  "organizationId": "string",
  "tenantId": "string",
  "code": "string",
  "name": "string",
  "symbol": null,
  "thousandsSeparator": null,
  "decimalSeparator": null
}
```

### Responses

**201** – Currency created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/currencies/currencies" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"organizationId\": \"string\",
  \"tenantId\": \"string\",
  \"code\": \"string\",
  \"name\": \"string\",
  \"symbol\": null,
  \"thousandsSeparator\": null,
  \"decimalSeparator\": null
}"
```

## PUT `/currencies/currencies`

Update currency

Updates an existing currency by id.

Requires features: currencies.manage

**Tags:** Currencies

**Requires authentication.**

**Features:** currencies.manage

### Request Body

Content-Type: `application/json`

```json
{
  "id": "string",
  "symbol": null,
  "thousandsSeparator": null,
  "decimalSeparator": null
}
```

### Responses

**200** – Currency updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/currencies/currencies" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\",
  \"symbol\": null,
  \"thousandsSeparator\": null,
  \"decimalSeparator\": null
}"
```

## GET `/currencies/currencies/options`

List currency options

Returns currencies formatted for select inputs.

Requires features: currencies.view

**Tags:** Currencies

**Requires authentication.**

**Features:** currencies.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| q | query | any | Optional |
| query | query | any | Optional |
| search | query | any | Optional |
| includeInactive | query | any | Optional |
| limit | query | any | Optional |

### Responses

**200** – Option list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "value": "string",
      "label": "string"
    }
  ]
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "items": []
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/currencies/currencies/options?limit=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/currencies/exchange-rates`

Delete exchangerate

Deletes an exchange rate by id.

Requires features: currencies.rates.manage

**Tags:** Currencies

**Requires authentication.**

**Features:** currencies.rates.manage

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – ExchangeRate deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/currencies/exchange-rates" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/currencies/exchange-rates`

List exchangerates

Returns a paginated collection of exchangerates scoped to the authenticated organization.

Requires features: currencies.rates.view

**Tags:** Currencies

**Requires authentication.**

**Features:** currencies.rates.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| fromCurrencyCode | query | any | Optional |
| toCurrencyCode | query | any | Optional |
| isActive | query | any | Optional |
| source | query | any | Optional |
| type | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated exchangerates

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "fromCurrencyCode": "string",
      "toCurrencyCode": "string",
      "rate": "string",
      "date": "string",
      "source": "string",
      "type": null,
      "isActive": true,
      "createdAt": null,
      "updatedAt": null,
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "tenantId": "00000000-0000-4000-8000-000000000000"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/currencies/exchange-rates?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/currencies/exchange-rates`

Create exchangerate

Creates a new exchange rate.

Requires features: currencies.rates.manage

**Tags:** Currencies

**Requires authentication.**

**Features:** currencies.rates.manage

### Request Body

Content-Type: `application/json`

```json
{
  "organizationId": "string",
  "tenantId": "string",
  "fromCurrencyCode": "string",
  "toCurrencyCode": "string",
  "rate": "string",
  "type": null
}
```

### Responses

**201** – ExchangeRate created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/currencies/exchange-rates" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"organizationId\": \"string\",
  \"tenantId\": \"string\",
  \"fromCurrencyCode\": \"string\",
  \"toCurrencyCode\": \"string\",
  \"rate\": \"string\",
  \"type\": null
}"
```

## PUT `/currencies/exchange-rates`

Update exchangerate

Updates an existing exchange rate by id.

Requires features: currencies.rates.manage

**Tags:** Currencies

**Requires authentication.**

**Features:** currencies.rates.manage

### Request Body

Content-Type: `application/json`

```json
{
  "id": "string",
  "type": null
}
```

### Responses

**200** – ExchangeRate updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/currencies/exchange-rates" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\",
  \"type\": null
}"
```

## DELETE `/currencies/fetch-configs`

Delete currency fetch configuration

Deletes a currency fetch configuration by id.

Requires features: currencies.fetch.manage

**Tags:** Currencies

**Requires authentication.**

**Features:** currencies.fetch.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. Currency fetch configuration identifier to delete |

### Responses

**200** – Currency fetch configuration deleted successfully

Content-Type: `application/json`

```json
{
  "success": true
}
```

**400** – Bad request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/currencies/fetch-configs?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/currencies/fetch-configs`

List currency fetch configurations

Returns all currency fetch configurations scoped to the authenticated organization.

Requires features: currencies.fetch.view

**Tags:** Currencies

**Requires authentication.**

**Features:** currencies.fetch.view

### Responses

**200** – A list of currency fetch configurations

Content-Type: `application/json`

```json
{
  "configs": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "provider": "string",
      "isEnabled": true,
      "syncTime": null,
      "lastSyncAt": null,
      "lastSyncStatus": null,
      "lastSyncMessage": null,
      "lastSyncCount": null,
      "config": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/currencies/fetch-configs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/currencies/fetch-configs`

Create currency fetch configuration

Creates a new currency fetch configuration.

Requires features: currencies.fetch.manage

**Tags:** Currencies

**Requires authentication.**

**Features:** currencies.fetch.manage

### Request Body

Content-Type: `application/json`

```json
{
  "provider": "NBP",
  "isEnabled": false,
  "syncTime": null,
  "config": null
}
```

### Responses

**201** – Currency fetch configuration created successfully

Content-Type: `application/json`

```json
{
  "config": {
    "id": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "provider": "string",
    "isEnabled": true,
    "syncTime": null,
    "lastSyncAt": null,
    "lastSyncStatus": null,
    "lastSyncMessage": null,
    "lastSyncCount": null,
    "config": null,
    "createdAt": "string",
    "updatedAt": "string"
  }
}
```

**400** – Bad request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/currencies/fetch-configs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"provider\": \"NBP\",
  \"isEnabled\": false,
  \"syncTime\": null,
  \"config\": null
}"
```

## PUT `/currencies/fetch-configs`

Update currency fetch configuration

Updates an existing currency fetch configuration by id.

Requires features: currencies.fetch.manage

**Tags:** Currencies

**Requires authentication.**

**Features:** currencies.fetch.manage

### Request Body

Content-Type: `application/json`

```json
{
  "syncTime": null,
  "config": null,
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Currency fetch configuration updated successfully

Content-Type: `application/json`

```json
{
  "config": {
    "id": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "provider": "string",
    "isEnabled": true,
    "syncTime": null,
    "lastSyncAt": null,
    "lastSyncStatus": null,
    "lastSyncMessage": null,
    "lastSyncCount": null,
    "config": null,
    "createdAt": "string",
    "updatedAt": "string"
  }
}
```

**400** – Bad request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/currencies/fetch-configs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"syncTime\": null,
  \"config\": null,
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## POST `/currencies/fetch-rates`

Fetch currency rates

Fetches currency exchange rates from configured providers for a specific date.

**Tags:** Currencies

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Currency rates fetched successfully

Content-Type: `application/json`

```json
{
  "totalFetched": 1,
  "byProvider": {
    "key": {
      "count": 1
    }
  },
  "errors": [
    "string"
  ]
}
```

**400** – Bad request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "totalFetched": 1,
  "byProvider": {
    "key": {
      "count": 1
    }
  },
  "errors": [
    "string"
  ]
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/currencies/fetch-rates" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## GET `/dashboards/layout`

Load the current dashboard layout

Returns the saved widget layout together with the widgets the current user is allowed to place.

Requires features: dashboards.view

**Tags:** Dashboards

**Requires authentication.**

**Features:** dashboards.view

### Responses

**200** – Current dashboard layout and available widgets.

Content-Type: `application/json`

```json
{
  "layout": {
    "items": [
      {
        "id": "00000000-0000-4000-8000-000000000000",
        "widgetId": "string",
        "order": 1
      }
    ]
  },
  "allowedWidgetIds": [
    "string"
  ],
  "canConfigure": true,
  "context": {
    "userId": "00000000-0000-4000-8000-000000000000",
    "tenantId": null,
    "organizationId": null,
    "userName": null,
    "userEmail": null,
    "userLabel": "string"
  },
  "widgets": [
    {
      "id": "string",
      "title": "string",
      "description": null,
      "defaultSize": "sm",
      "defaultEnabled": true,
      "defaultSettings": null,
      "features": [
        "string"
      ],
      "moduleId": "string",
      "icon": null,
      "loaderKey": "string",
      "supportsRefresh": true
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dashboards/layout" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/dashboards/layout`

Persist dashboard layout changes

Saves the provided widget ordering, sizes, and settings for the current user.

Requires features: dashboards.configure

**Tags:** Dashboards

**Requires authentication.**

**Features:** dashboards.configure

### Request Body

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "widgetId": "string",
      "order": 1
    }
  ]
}
```

### Responses

**200** – Layout updated successfully.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid layout payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/dashboards/layout" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"items\": [
    {
      \"id\": \"00000000-0000-4000-8000-000000000000\",
      \"widgetId\": \"string\",
      \"order\": 1
    }
  ]
}"
```

## PATCH `/dashboards/layout/{itemId}`

Update a dashboard layout item

Adjusts the size or settings for a single widget within the dashboard layout.

Requires features: dashboards.configure

**Tags:** Dashboards

**Requires authentication.**

**Features:** dashboards.configure

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| itemId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Layout item updated.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload or missing item id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Item not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/dashboards/layout/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## GET `/dashboards/roles/widgets`

Fetch widget assignments for a role

Returns the widgets explicitly assigned to the given role together with the evaluation scope.

Requires features: dashboards.admin.assign-widgets

**Tags:** Dashboards

**Requires authentication.**

**Features:** dashboards.admin.assign-widgets

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| roleId | query | any | Required |
| tenantId | query | any | Optional |
| organizationId | query | any | Optional |

### Responses

**200** – Current widget configuration for the role.

Content-Type: `application/json`

```json
{
  "widgetIds": [
    "string"
  ],
  "hasCustom": true,
  "scope": {
    "tenantId": null,
    "organizationId": null
  }
}
```

**400** – Missing role identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dashboards/roles/widgets?roleId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/dashboards/roles/widgets`

Update widgets assigned to a role

Persists the widget list for a role within the provided tenant and organization scope.

Requires features: dashboards.admin.assign-widgets

**Tags:** Dashboards

**Requires authentication.**

**Features:** dashboards.admin.assign-widgets

### Request Body

Content-Type: `application/json`

```json
{
  "roleId": "00000000-0000-4000-8000-000000000000",
  "widgetIds": [
    "string"
  ]
}
```

### Responses

**200** – Widgets updated successfully.

Content-Type: `application/json`

```json
{
  "ok": true,
  "widgetIds": [
    "string"
  ]
}
```

**400** – Invalid payload or unknown widgets

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/dashboards/roles/widgets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"roleId\": \"00000000-0000-4000-8000-000000000000\",
  \"widgetIds\": [
    \"string\"
  ]
}"
```

## GET `/dashboards/users/widgets`

Read widget overrides for a user

Returns the widgets inherited and explicitly configured for the requested user within the current scope.

Requires features: dashboards.admin.assign-widgets

**Tags:** Dashboards

**Requires authentication.**

**Features:** dashboards.admin.assign-widgets

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| userId | query | any | Required |
| tenantId | query | any | Optional |
| organizationId | query | any | Optional |

### Responses

**200** – Widget settings for the user.

Content-Type: `application/json`

```json
{
  "mode": "inherit",
  "widgetIds": [
    "string"
  ],
  "hasCustom": true,
  "effectiveWidgetIds": [
    "string"
  ],
  "scope": {
    "tenantId": null,
    "organizationId": null
  }
}
```

**400** – Missing user identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dashboards/users/widgets?userId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/dashboards/users/widgets`

Update user-specific dashboard widgets

Sets the widget override mode and allowed widgets for a user. Passing `mode: inherit` clears overrides.

Requires features: dashboards.admin.assign-widgets

**Tags:** Dashboards

**Requires authentication.**

**Features:** dashboards.admin.assign-widgets

### Request Body

Content-Type: `application/json`

```json
{
  "userId": "00000000-0000-4000-8000-000000000000",
  "mode": "inherit",
  "widgetIds": [
    "string"
  ]
}
```

### Responses

**200** – Overrides saved.

Content-Type: `application/json`

```json
{
  "ok": true,
  "mode": "inherit",
  "widgetIds": [
    "string"
  ]
}
```

**400** – Invalid payload or unknown widgets

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/dashboards/users/widgets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"userId\": \"00000000-0000-4000-8000-000000000000\",
  \"mode\": \"inherit\",
  \"widgetIds\": [
    \"string\"
  ]
}"
```

## GET `/dashboards/widgets/catalog`

List available dashboard widgets

Returns the catalog of widgets that modules expose, including defaults and feature requirements.

Requires features: dashboards.admin.assign-widgets

**Tags:** Dashboards

**Requires authentication.**

**Features:** dashboards.admin.assign-widgets

### Responses

**200** – Widgets available for assignment.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "title": "string",
      "description": null,
      "defaultSize": "sm",
      "defaultEnabled": true,
      "defaultSettings": null,
      "features": [
        "string"
      ],
      "moduleId": "string",
      "icon": null,
      "loaderKey": "string",
      "supportsRefresh": true
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dashboards/widgets/catalog" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/dashboards/widgets/data`

Fetch aggregated data for dashboard widgets

Executes an aggregation query against the specified entity type and returns the result. Supports date range filtering, grouping, and period-over-period comparison.

Requires features: analytics.view

**Tags:** Dashboards

**Requires authentication.**

**Features:** analytics.view

### Request Body

Content-Type: `application/json`

```json
{
  "entityType": "string",
  "metric": {
    "field": "string",
    "aggregate": "count"
  }
}
```

### Responses

**200** – Aggregated data for the widget.

Content-Type: `application/json`

```json
{
  "value": null,
  "data": [
    {
      "value": null
    }
  ],
  "metadata": {
    "fetchedAt": "string",
    "recordCount": 1
  }
}
```

**400** – Invalid request payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/dashboards/widgets/data" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityType\": \"string\",
  \"metric\": {
    \"field\": \"string\",
    \"aggregate\": \"count\"
  }
}"
```

## POST `/dashboards/widgets/data/batch`

Fetch aggregated data for multiple dashboard widgets in one request

Resolves a batch of widget data requests with a single authentication, RBAC, organization-scope, and database-context setup. Each request is keyed by an opaque widget id and resolved independently, so a failure in one widget does not fail the batch.

Requires features: analytics.view

**Tags:** Dashboards

**Requires authentication.**

**Features:** analytics.view

### Request Body

Content-Type: `application/json`

```json
{
  "requests": [
    {
      "id": "string",
      "request": {
        "entityType": "string",
        "metric": {
          "field": "string",
          "aggregate": "count"
        }
      }
    }
  ]
}
```

### Responses

**200** – Per-widget aggregation results keyed by request id.

Content-Type: `application/json`

```json
{
  "results": [
    {
      "id": "string",
      "ok": true,
      "data": {
        "value": null,
        "data": [
          {
            "value": null
          }
        ],
        "metadata": {
          "fetchedAt": "string",
          "recordCount": 1
        }
      }
    }
  ]
}
```

**400** – Invalid request payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/dashboards/widgets/data/batch" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"requests\": [
    {
      \"id\": \"string\",
      \"request\": {
        \"entityType\": \"string\",
        \"metric\": {
          \"field\": \"string\",
          \"aggregate\": \"count\"
        }
      }
    }
  ]
}"
```

## GET `/data_exports/admin`

List data export requests (admin)

Returns a paginated list of data-export requests scoped to the caller tenant/organization. Supports filtering by status (csv) and channel (csv), free-text search on the subject by email or name, and sorting by created/assembled/delivered time.

Requires features: data_exports.admin.list

**Tags:** Admin · Data Exports

**Requires authentication.**

**Features:** data_exports.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Required |
| channel | query | any | Required |
| search | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sort | query | any | Optional |

### Responses

**200** – Data export request list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "status": "requested",
      "channel": "self_service",
      "subjectUserId": null,
      "rejectionReason": null,
      "createdAt": "string",
      "assembledAt": null,
      "deliveredAt": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/data_exports/admin?page=1&pageSize=50&sort=createdAt%3Adesc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/data_exports/admin`

Trigger a data export for a chosen user (admin)

Creates a data-export request on the `admin_initiated` channel for an existing consumer in the caller tenant/organization and enqueues automated assembly. Delivery still waits for the human Send gate. Fails with 404 if the subject is not a live consumer in scope.

Requires features: data_exports.admin.trigger

**Tags:** Admin · Data Exports

**Requires authentication.**

**Features:** data_exports.admin.trigger

### Request Body

Content-Type: `application/json`

```json
{
  "subjectUserId": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**201** – Export request created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "requested"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Subject not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/data_exports/admin" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"subjectUserId\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/data_exports/admin/{id}`

Get a data export request (admin)

Returns a single data-export request scoped to the caller tenant/organization, including the resolved subject (minimal profile), assembly/bundle status, applied override, and rejection reason.

Requires features: data_exports.admin.view

**Tags:** Admin · Data Exports

**Requires authentication.**

**Features:** data_exports.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Data export request detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "requested",
  "channel": "self_service",
  "subjectUserId": null,
  "subject": null,
  "identifierType": null,
  "identifierValue": null,
  "overrideItems": null,
  "rejectionReason": null,
  "hasBundle": true,
  "linkExpiresAt": null,
  "requestedByUserId": null,
  "sentByUserId": null,
  "assembledAt": null,
  "deliveredAt": null,
  "createdAt": "string",
  "updatedAt": "string"
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Request not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/data_exports/admin/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/data_exports/admin/{id}/preview`

Preview/download an assembled data export bundle (admin)

Serves the assembled zip bundle (PDF + CSV/JSON) for admin review before sending, scoped to the caller tenant/organization. S3-backed partitions return a 302 redirect to a short-lived presigned URL; on-disk dev/test partitions stream the zip inline. Available from the `assembled` state onward. Fails with 409 if the bundle is not yet assembled.

Requires features: data_exports.admin.preview

**Tags:** Admin · Data Exports

**Requires authentication.**

**Features:** data_exports.admin.preview

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Zip bundle (streamed inline for on-disk partitions)

Content-Type: `application/zip`

**302** – Redirect to a short-lived presigned download URL

Content-Type: `application/json`

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Request not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Bundle not assembled

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/data_exports/admin/00000000-0000-4000-8000-000000000000/preview" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/data_exports/admin/{id}/reject`

Reject a data export request (admin)

Closes a data-export request without fulfilment and records the reason (internal-only, never exported to the subject per Art. 15(4)). Valid from any pre-delivery state; transitions the request to `rejected`. Fails with 409 if the request is already delivered or rejected.

Requires features: data_exports.admin.reject

**Tags:** Admin · Data Exports

**Requires authentication.**

**Features:** data_exports.admin.reject

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "reason": "string"
}
```

### Responses

**200** – Request rejected

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "rejected"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Request not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Request cannot be rejected in its current state

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/data_exports/admin/00000000-0000-4000-8000-000000000000/reject" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"reason\": \"string\"
}"
```

## POST `/data_exports/admin/{id}/send`

Review and send an assembled data export (admin)

Releases an already-assembled export to the subject: mints the secure 24h download token, fires the in-app "export ready" notification, and transitions the request from `assembled` to `delivered`. Fails with 409 if the request is not in the `assembled` state with a stored bundle.

Requires features: data_exports.admin.send

**Tags:** Admin · Data Exports

**Requires authentication.**

**Features:** data_exports.admin.send

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Export delivered

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "delivered"
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Request not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Export not ready to send

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/data_exports/admin/00000000-0000-4000-8000-000000000000/send" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/data_exports/admin/config`

Get the effective data export configuration

Returns the effective per-tenant checklist configuration: each item key mapped to its enabled flag (checklist defaults overlaid by stored deviations), plus the list of gated item keys that are clamped OFF pending legal sign-off.

Requires features: data_exports.config.view

**Tags:** Data Exports

**Requires authentication.**

**Features:** data_exports.config.view

### Responses

**200** – Effective configuration

Content-Type: `application/json`

```json
{
  "enabledItems": {
    "key": true
  },
  "gatedItems": [
    "string"
  ],
  "items": [
    {
      "key": "string",
      "domain": "string",
      "titleKey": "string",
      "gated": true,
      "enabled": true
    }
  ]
}
```

**500** – Failed to load configuration

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/data_exports/admin/config" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/data_exports/admin/config`

Update the data export configuration (admin)

Persists the per-tenant checklist deviations (sparse enabledItems map; keys must belong to the registered taxonomy). Returns the freshly resolved effective config. Gated items remain clamped OFF regardless of what is posted.

Requires features: data_exports.config.update

**Tags:** Data Exports

**Requires authentication.**

**Features:** data_exports.config.update

### Request Body

Content-Type: `application/json`

```json
{
  "enabledItems": {
    "key": true
  }
}
```

### Responses

**200** – Updated effective configuration

Content-Type: `application/json`

```json
{
  "enabledItems": {
    "key": true
  },
  "gatedItems": [
    "string"
  ]
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to update configuration

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/data_exports/admin/config" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"enabledItems\": {
    \"key\": true
  }
}"
```

## GET `/data_exports/exports/{id}/download`

Download a delivered data export bundle

Serves the zip bundle (PDF + CSV/JSON) for a delivered export. S3-backed partitions return a 302 redirect to a short-lived presigned URL (the bundle bytes never pass through the backend); on-disk dev/test partitions return the body inline. Access is gated solely by a signed, unexpired download token (`t`) whose request id matches the path id — no session is required. Returns 403 for an invalid/expired/mismatched token and 404 when the export is not delivered or no longer available.

**Tags:** Data Exports

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| t | query | any | Required |

### Responses

**200** – Zip bundle (returned inline for on-disk partitions)

Content-Type: `application/zip`

**302** – Redirect to a short-lived presigned download URL

Content-Type: `application/json`

**400** – Invalid id or token

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Invalid or expired link

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Export not found or not delivered

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load export

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/data_exports/exports/00000000-0000-4000-8000-000000000000/download?t=string" \
  -H "Accept: application/json"
```

## POST `/data_exports/me/exports`

Request a personal data export

Lets the authenticated user trigger their own GDPR data export (in-app self-service channel). Requires a recent re-authentication via email OTP or social ID token. Creates a data-export request in the `requested` state and enqueues automated assembly; the request parks at `assembled` and is not delivered until an admin reviews and sends it.

Requires features: data_exports.request.create

**Tags:** Data Exports

**Requires authentication.**

**Features:** data_exports.request.create

### Request Body

Content-Type: `application/json`

```json
{
  "reauth": {
    "method": "otp",
    "code": "string"
  }
}
```

### Responses

**202** – Export request accepted and queued for assembly

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "requested"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – User not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to request data export

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/data_exports/me/exports" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"reauth\": {
    \"method\": \"otp\",
    \"code\": \"string\"
  }
}"
```

## GET `/dictionaries`

List dictionaries

Returns dictionaries accessible to the current organization, optionally including inactive records.

Requires features: dictionaries.view

**Tags:** Dictionaries

**Requires authentication.**

**Features:** dictionaries.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| includeInactive | query | any | Optional |

### Responses

**200** – Dictionary collection.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "key": "string",
      "name": "string",
      "description": null,
      "isSystem": true,
      "isActive": true,
      "managerVisibility": null,
      "organizationId": null,
      "createdAt": "string",
      "updatedAt": null
    }
  ]
}
```

**500** – Failed to load dictionaries

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dictionaries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/dictionaries`

Create dictionary

Registers a dictionary scoped to the current organization.

Requires features: dictionaries.manage

**Tags:** Dictionaries

**Requires authentication.**

**Features:** dictionaries.manage

### Request Body

Content-Type: `application/json`

```json
{
  "key": "string",
  "name": "string"
}
```

### Responses

**201** – Dictionary created.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "key": "string",
  "name": "string",
  "description": null,
  "isSystem": true,
  "isActive": true,
  "managerVisibility": null,
  "organizationId": null,
  "createdAt": "string",
  "updatedAt": null
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Dictionary key already exists

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to create dictionary

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/dictionaries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"key\": \"string\",
  \"name\": \"string\"
}"
```

## DELETE `/dictionaries/{dictionaryId}`

Delete dictionary

Soft deletes the dictionary unless it is the protected currency dictionary.

Requires features: dictionaries.manage

**Tags:** Dictionaries

**Requires authentication.**

**Features:** dictionaries.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |

### Responses

**200** – Dictionary archived.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Protected dictionary cannot be deleted

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to delete dictionary

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/dictionaries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/dictionaries/{dictionaryId}`

Get dictionary

Returns details for the specified dictionary, including inheritance flags.

Requires features: dictionaries.view

**Tags:** Dictionaries

**Requires authentication.**

**Features:** dictionaries.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |

### Responses

**200** – Dictionary details.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "key": "string",
  "name": "string",
  "description": null,
  "isSystem": true,
  "isActive": true,
  "managerVisibility": null,
  "organizationId": null,
  "createdAt": "string",
  "updatedAt": null
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load dictionary

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dictionaries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/dictionaries/{dictionaryId}`

Update dictionary

Updates mutable attributes of the dictionary. Currency dictionaries are protected from modification.

Requires features: dictionaries.manage

**Tags:** Dictionaries

**Requires authentication.**

**Features:** dictionaries.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Dictionary updated.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "key": "string",
  "name": "string",
  "description": null,
  "isSystem": true,
  "isActive": true,
  "managerVisibility": null,
  "organizationId": null,
  "createdAt": "string",
  "updatedAt": null
}
```

**400** – Validation failed or protected dictionary

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Dictionary key already exists

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to update dictionary

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/dictionaries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## GET `/dictionaries/{dictionaryId}/entries`

List dictionary entries

Returns entries for the specified dictionary ordered by its configured entry sort mode.

Requires features: dictionaries.view

**Tags:** Dictionaries

**Requires authentication.**

**Features:** dictionaries.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |

### Responses

**200** – Dictionary entries.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "value": "string",
      "label": "string",
      "color": null,
      "icon": null,
      "position": 1,
      "isDefault": true,
      "createdAt": "string",
      "updatedAt": null
    }
  ]
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load dictionary entries

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dictionaries/00000000-0000-4000-8000-000000000000/entries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/dictionaries/{dictionaryId}/entries`

Create dictionary entry

Creates a new entry in the specified dictionary.

Requires features: dictionaries.manage

**Tags:** Dictionaries

**Requires authentication.**

**Features:** dictionaries.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "value": "string",
  "color": null,
  "icon": null
}
```

### Responses

**201** – Dictionary entry created.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "value": "string",
  "label": "string",
  "color": null,
  "icon": null,
  "position": 1,
  "isDefault": true,
  "createdAt": "string",
  "updatedAt": null
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to create dictionary entry

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/dictionaries/00000000-0000-4000-8000-000000000000/entries" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"value\": \"string\",
  \"color\": null,
  \"icon\": null
}"
```

## DELETE `/dictionaries/{dictionaryId}/entries/{entryId}`

Delete dictionary entry

Deletes the specified dictionary entry via the command bus.

Requires features: dictionaries.manage

**Tags:** Dictionaries

**Requires authentication.**

**Features:** dictionaries.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |
| entryId | path | any | Required |

### Responses

**200** – Entry deleted.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary or entry not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to delete entry

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/dictionaries/00000000-0000-4000-8000-000000000000/entries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/dictionaries/{dictionaryId}/entries/{entryId}`

Update dictionary entry

Updates the specified dictionary entry using the command bus pipeline.

Requires features: dictionaries.manage

**Tags:** Dictionaries

**Requires authentication.**

**Features:** dictionaries.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |
| entryId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "color": null,
  "icon": null
}
```

### Responses

**200** – Dictionary entry updated.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "value": "string",
  "label": "string",
  "color": null,
  "icon": null,
  "position": 1,
  "isDefault": true,
  "createdAt": "string",
  "updatedAt": null
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary or entry not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to update entry

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/dictionaries/00000000-0000-4000-8000-000000000000/entries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"color\": null,
  \"icon\": null
}"
```

## POST `/dictionaries/{dictionaryId}/entries/reorder`

Reorder dictionary entries

Updates the position of dictionary entries for drag-and-drop reordering.

Requires features: dictionaries.manage

**Tags:** Dictionaries

**Requires authentication.**

**Features:** dictionaries.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "entries": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "position": 1
    }
  ]
}
```

### Responses

**200** – Entries reordered.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to reorder entries

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/dictionaries/00000000-0000-4000-8000-000000000000/entries/reorder" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entries\": [
    {
      \"id\": \"00000000-0000-4000-8000-000000000000\",
      \"position\": 1
    }
  ]
}"
```

## POST `/dictionaries/{dictionaryId}/entries/set-default`

Set default dictionary entry

Marks the specified entry as the default for this dictionary, clearing any previous default.

Requires features: dictionaries.manage

**Tags:** Dictionaries

**Requires authentication.**

**Features:** dictionaries.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| dictionaryId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "entryId": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Default entry set.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Dictionary or entry not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to set default entry

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/dictionaries/00000000-0000-4000-8000-000000000000/entries/set-default" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entryId\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/directory/organization-branding`

Read sidebar branding for the selected organization

Returns the logo URL used by the backend sidebar for the currently selected organization.

Requires features: directory.organizations.view

**Tags:** Directory

**Requires authentication.**

**Features:** directory.organizations.view

### Responses

**200** – Organization branding

Content-Type: `application/json`

```json
{
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "organizationName": "string",
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "logoUrl": null,
  "updatedAt": null
}
```

**400** – A concrete organization scope is required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Organization not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/directory/organization-branding" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/directory/organization-branding`

Update sidebar branding for the selected organization

Stores an external image URL or an internal attachment image URL as the selected organization logo.

Requires features: directory.organizations.manage

**Tags:** Directory

**Requires authentication.**

**Features:** directory.organizations.manage

### Request Body

Content-Type: `application/json`

```json
{
  "logoUrl": null
}
```

### Responses

**200** – Updated organization branding

Content-Type: `application/json`

```json
{
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "organizationName": "string",
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "logoUrl": null,
  "updatedAt": null
}
```

**400** – Save failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Organization branding changed since it was loaded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Invalid logo URL

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/directory/organization-branding" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"logoUrl\": null
}"
```

## GET `/directory/organization-switcher`

Load organization switcher menu

Returns the hierarchical menu of organizations the current user may switch to within the active tenant.

**Tags:** Directory

**Requires authentication.**

### Responses

**200** – Organization switcher payload.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "depth": 1,
      "selectable": true,
      "children": []
    }
  ],
  "selectedId": null,
  "canManage": true,
  "canViewAllOrganizations": true,
  "tenantId": null,
  "tenants": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "isActive": true
    }
  ],
  "isSuperAdmin": true
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/directory/organization-switcher" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/directory/organizations`

Delete organization

Soft deletes an organization identified by id.

Requires features: directory.organizations.manage

**Tags:** Directory

**Requires authentication.**

**Features:** directory.organizations.manage

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Organization deleted.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/directory/organizations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/directory/organizations`

List organizations

Returns organizations using options, tree, or paginated manage view depending on the `view` parameter.

Requires features: directory.organizations.view

**Tags:** Directory

**Requires authentication.**

**Features:** directory.organizations.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| view | query | any | Optional |
| ids | query | any | Optional |
| tenantId | query | any | Optional |
| includeInactive | query | any | Optional |
| status | query | any | Optional |

### Responses

**200** – Organization data for the requested view.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "parentId": null,
      "parentName": null,
      "tenantId": null,
      "tenantName": null,
      "rootId": null,
      "treePath": null
    }
  ]
}
```

**400** – Invalid query or tenant scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/directory/organizations?page=1&pageSize=50&view=options" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/directory/organizations`

Create organization

Creates a new organization within a tenant and optionally assigns hierarchy relationships.

Requires features: directory.organizations.manage

**Tags:** Directory

**Requires authentication.**

**Features:** directory.organizations.manage

### Request Body

Content-Type: `application/json`

```json
{
  "name": "string",
  "slug": null,
  "logoUrl": null,
  "parentId": null
}
```

### Responses

**201** – Organization created.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/directory/organizations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\",
  \"slug\": null,
  \"logoUrl\": null,
  \"parentId\": null
}"
```

## PUT `/directory/organizations`

Update organization

Updates organization details and hierarchy assignments.

Requires features: directory.organizations.manage

**Tags:** Directory

**Requires authentication.**

**Features:** directory.organizations.manage

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "slug": null,
  "logoUrl": null,
  "parentId": null
}
```

### Responses

**200** – Organization updated.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/directory/organizations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"slug\": null,
  \"logoUrl\": null,
  \"parentId\": null
}"
```

## GET `/directory/organizations/lookup`

Public organization lookup by slug

**Tags:** Directory (Tenants & Organizations)

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/directory/organizations/lookup" \
  -H "Accept: application/json"
```

## DELETE `/directory/tenants`

Delete tenant

Soft deletes the tenant identified by id.

Requires features: directory.tenants.manage

**Tags:** Directory

**Requires authentication.**

**Features:** directory.tenants.manage

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Tenant removed.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/directory/tenants" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/directory/tenants`

List tenants

Returns tenants visible to the current user with optional search and pagination.

Requires features: directory.tenants.view

**Tags:** Directory

**Requires authentication.**

**Features:** directory.tenants.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| isActive | query | any | Optional |

### Responses

**200** – Paged list of tenants.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "isActive": true,
      "createdAt": null,
      "updatedAt": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/directory/tenants?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/directory/tenants`

Create tenant

Creates a new tenant and returns its identifier.

Requires features: directory.tenants.manage

**Tags:** Directory

**Requires authentication.**

**Features:** directory.tenants.manage

### Request Body

Content-Type: `application/json`

```json
{
  "name": "string"
}
```

### Responses

**201** – Tenant created.

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/directory/tenants" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\"
}"
```

## PUT `/directory/tenants`

Update tenant

Updates tenant properties such as name or activation state.

Requires features: directory.tenants.manage

**Tags:** Directory

**Requires authentication.**

**Features:** directory.tenants.manage

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Tenant updated.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/directory/tenants" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/directory/tenants/lookup`

Public tenant lookup

**Tags:** Directory (Tenants & Organizations)

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/directory/tenants/lookup" \
  -H "Accept: application/json"
```

## GET `/dns_filtering/admin/child-safety`

List families with their child-safety state

Returns paginated families with content/safe-web flags, derived protectionStatus, child count, and last updated timestamp. Owner email and name are joined. Free-text search matches the family owner by email or name. When the query is a UUID it also matches the family id and owner user id.

Requires features: dns_filtering.admin.list

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| search | query | any | Optional |
| protectionStatus | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – List of families

Content-Type: `application/json`

```json
{
  "items": [
    {
      "familyId": "string",
      "ownerUserId": "string",
      "ownerEmail": "string",
      "ownerName": null,
      "contentFilterEnabled": true,
      "safeWebEnabled": true,
      "protectionStatus": "fully",
      "childCount": 1,
      "lastUpdatedAt": null
    }
  ],
  "total": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dns_filtering/admin/child-safety?limit=50&offset=0" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/dns_filtering/admin/child-safety/{familyId}`

Get child-safety detail for a family

Returns the family child-safety settings, the owner, and the list of affected child users (with email + name).

Requires features: dns_filtering.admin.list

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| familyId | path | any | Required |

### Responses

**200** – Child safety detail

Content-Type: `application/json`

```json
{
  "familyId": "string",
  "ownerUserId": "string",
  "ownerEmail": "string",
  "ownerName": null,
  "contentFilterEnabled": true,
  "safeWebEnabled": true,
  "protectionStatus": "fully",
  "lastUpdatedAt": null,
  "children": [
    {
      "userId": "string",
      "email": null,
      "name": null
    }
  ]
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dns_filtering/admin/child-safety/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/dns_filtering/admin/child-safety/{familyId}`

Mock-set family child-safety toggles (QA tool)

Admin/QA tool. Sets contentFilterEnabled and/or safeWebEnabled for every child member of the family, with no caller-ownership guard. Does not emit events or push — use the refresh-push endpoint to notify devices. Requires the dns_filtering.admin.mock feature.

Requires features: dns_filtering.admin.mock

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.mock

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| familyId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Updated child safety settings

Content-Type: `application/json`

```json
{
  "contentFilterEnabled": true,
  "safeWebEnabled": true,
  "protectionStatus": "fully"
}
```

**400** – Invalid params or body

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Family has no child members to apply settings to

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/dns_filtering/admin/child-safety/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/dns_filtering/admin/child-safety/{familyId}/refresh-push`

Send a silent DNS-config refresh push to every child (QA tool)

Fans a data-only `dns_config_changed` push (`action: refresh_dns_config`) out to every `role:'child'` member of the family. Response carries aggregate per-device delivery counts plus the recipient (child) count. Requires the dns_filtering.admin.mock feature.

Requires features: dns_filtering.admin.mock

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.mock

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| familyId | path | any | Required |

### Responses

**200** – Refresh push dispatched to children

Content-Type: `application/json`

```json
{
  "ok": true,
  "sent": 1,
  "pruned": 1,
  "failed": 1,
  "recipients": 1
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/dns_filtering/admin/child-safety/00000000-0000-4000-8000-000000000000/refresh-push" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/dns_filtering/admin/config`

Get DNS filtering pricing and DNS provider catalog

Requires features: dns_filtering.admin.list

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.list

### Responses

**200** – DNS filtering config

Content-Type: `application/json`

```json
{
  "networkTurboPriceEuroCents": 1,
  "providers": [
    {
      "id": "string",
      "label": null,
      "dohUrl": "https://example.com/resource",
      "dnsIpPrimary": "string",
      "dnsIpSecondary": "string",
      "isActive": true
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dns_filtering/admin/config" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/dns_filtering/admin/config`

Update DNS filtering pricing

Updates the singleton DnsFilteringConfig row's networkTurboPriceEuroCents. Action is logged with before/after snapshots for undo.

Requires features: dns_filtering.admin.config

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.config

### Request Body

Content-Type: `application/json`

```json
{
  "networkTurboPriceEuroCents": 1
}
```

### Responses

**200** – Updated DNS filtering config

Content-Type: `application/json`

```json
{
  "networkTurboPriceEuroCents": 1,
  "providers": [
    {
      "id": "string",
      "label": null,
      "dohUrl": "https://example.com/resource",
      "dnsIpPrimary": "string",
      "dnsIpSecondary": "string",
      "isActive": true
    }
  ]
}
```

**400** – Invalid body

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/dns_filtering/admin/config" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"networkTurboPriceEuroCents\": 1
}"
```

## GET `/dns_filtering/admin/network-turbo`

List Network Turbo records across the tenant

Returns users with Network Turbo records, with their effective state plus per-source breakdown (self / family-admin). Supports filtering by status, fundedBy, autoRenewal, familyId, userId, and free-text user search by email or name (a UUID query also matches the user id).

Requires features: dns_filtering.admin.list

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| search | query | any | Optional |
| status | query | any | Optional |
| fundedBy | query | any | Optional |
| autoRenewal | query | any | Optional |
| familyId | query | any | Optional |
| userId | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – List of users with Network Turbo records

Content-Type: `application/json`

```json
{
  "items": [
    {
      "userId": "string",
      "userEmail": "string",
      "userName": null,
      "familyId": null,
      "effectiveEnabled": true,
      "effectiveFundedBy": null,
      "effectivePaidUntil": null,
      "autoRenewalEnabled": true,
      "self": {
        "state": "string",
        "paidUntil": null,
        "bankedUntil": null,
        "pausedAt": null,
        "autoRenewalEnabled": true
      },
      "familyAdmin": {
        "state": "string",
        "paidUntil": null,
        "bankedUntil": null,
        "pausedAt": null,
        "autoRenewalEnabled": true
      }
    }
  ],
  "total": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dns_filtering/admin/network-turbo?limit=50&offset=0" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/dns_filtering/admin/network-turbo-prices`

List Network Turbo per-currency price overrides

Returns the active per-display-currency Network Turbo price overrides (global config), ordered by currency. Currencies without a row fall back to the EUR default converted and charm-rounded.

Requires features: dns_filtering.admin.network_turbo_prices

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.network_turbo_prices

### Responses

**200** – Network Turbo prices

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "currency": "string",
      "amountMinorUnits": 1,
      "isActive": true
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dns_filtering/admin/network-turbo-prices" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/dns_filtering/admin/network-turbo-prices`

Create a Network Turbo per-currency price override

Creates a per-currency override that becomes the actual price charged for users displaying that currency (the EUR debited floats, computed fresh at each charge). EUR is rejected (the EUR default is configured via the pricing config). Returns 409 if a non-deleted override already exists for the currency. Action is logged with an undo snapshot.

Requires features: dns_filtering.admin.network_turbo_prices

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.network_turbo_prices

### Request Body

Content-Type: `application/json`

```json
{
  "amountMinorUnits": 1,
  "isActive": true
}
```

### Responses

**201** – Created override

Content-Type: `application/json`

```json
{
  "id": "string",
  "currency": "string",
  "amountMinorUnits": 1,
  "isActive": true
}
```

**400** – Invalid body

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – An override already exists for this currency

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/dns_filtering/admin/network-turbo-prices" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"amountMinorUnits\": 1,
  \"isActive\": true
}"
```

## DELETE `/dns_filtering/admin/network-turbo-prices/{id}`

Delete a Network Turbo per-currency price override

Soft-deletes the override; users displaying that currency revert to the EUR default converted and charm-rounded. Action is logged with an undo snapshot.

Requires features: dns_filtering.admin.network_turbo_prices

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.network_turbo_prices

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Deleted

Content-Type: `application/json`

```json
{
  "id": "string"
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/dns_filtering/admin/network-turbo-prices/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/dns_filtering/admin/network-turbo-prices/{id}`

Get a Network Turbo per-currency price override

Requires features: dns_filtering.admin.network_turbo_prices

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.network_turbo_prices

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Override

Content-Type: `application/json`

```json
{
  "id": "string",
  "currency": "string",
  "amountMinorUnits": 1,
  "isActive": true
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dns_filtering/admin/network-turbo-prices/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/dns_filtering/admin/network-turbo-prices/{id}`

Update a Network Turbo per-currency price override

Updates the local amount and/or active flag. The currency (the override key) is immutable. Action is logged with an undo snapshot.

Requires features: dns_filtering.admin.network_turbo_prices

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.network_turbo_prices

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Updated override

Content-Type: `application/json`

```json
{
  "id": "string",
  "currency": "string",
  "amountMinorUnits": 1,
  "isActive": true
}
```

**400** – Invalid params/body

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/dns_filtering/admin/network-turbo-prices/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## GET `/dns_filtering/admin/network-turbo/{userId}`

Get Network Turbo detail + resolved DNS config for a user

Returns the full per-source Network Turbo breakdown plus the user's currently-resolved DNS provider/reason/enforcedBy. Scoped to the caller's organization: a user from another company reads as not found.

Requires features: dns_filtering.admin.list

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| userId | path | any | Required |

### Responses

**200** – Network Turbo detail

Content-Type: `application/json`

```json
{
  "userId": "string",
  "userEmail": "string",
  "userName": null,
  "familyId": null,
  "networkTurbo": {
    "enabled": true,
    "fundedBy": null,
    "paidUntil": null,
    "autoRenewalEnabled": true,
    "self": {
      "state": "string",
      "paidUntil": null,
      "bankedUntil": null,
      "pausedAt": null,
      "autoRenewalEnabled": true
    },
    "familyAdmin": {
      "state": "string",
      "paidUntil": null,
      "bankedUntil": null,
      "pausedAt": null,
      "autoRenewalEnabled": true
    }
  },
  "dnsConfig": null
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – User not found or out of scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dns_filtering/admin/network-turbo/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/dns_filtering/admin/network-turbo/{userId}`

Mock-set a user Network Turbo source (QA tool)

Admin/QA tool. Directly sets the user's `self` or `family-admin` Network Turbo row to active (one-year paid window) or disabled, with NO wallet charge and NO freeze/handoff logic. Does not emit events or push — use the refresh-push endpoint to notify the device. Requires the dns_filtering.admin.mock feature.

Requires features: dns_filtering.admin.mock

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.mock

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| userId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "fundedBy": "self",
  "enabled": true
}
```

### Responses

**200** – Updated Network Turbo status for the user

Content-Type: `application/json`

```json
{
  "enabled": true,
  "fundedBy": null,
  "paidUntil": null,
  "autoRenewalEnabled": true,
  "self": {
    "state": "string",
    "paidUntil": null,
    "bankedUntil": null,
    "pausedAt": null,
    "autoRenewalEnabled": true
  },
  "familyAdmin": {
    "state": "string",
    "paidUntil": null,
    "bankedUntil": null,
    "pausedAt": null,
    "autoRenewalEnabled": true
  },
  "priceEuroCents": 1
}
```

**400** – Invalid params or body

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/dns_filtering/admin/network-turbo/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"fundedBy\": \"self\",
  \"enabled\": true
}"
```

## POST `/dns_filtering/admin/network-turbo/{userId}/refresh-push`

Send a silent DNS-config refresh push (QA tool)

Fires a data-only `dns_config_changed` push (`action: refresh_dns_config`) at the user, telling the app to re-fetch /api/users/me/dns-config. Response carries per-device delivery counts. Requires the dns_filtering.admin.mock feature.

Requires features: dns_filtering.admin.mock

**Tags:** DNS Filtering Admin

**Requires authentication.**

**Features:** dns_filtering.admin.mock

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| userId | path | any | Required |

### Responses

**200** – Refresh push dispatched

Content-Type: `application/json`

```json
{
  "ok": true,
  "sent": 1,
  "pruned": 1,
  "failed": 1
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/dns_filtering/admin/network-turbo/00000000-0000-4000-8000-000000000000/refresh-push" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/dns_filtering/admin/workers/charge-network-turbo`

Run charge-network-turbo worker inline

Executes the hourly Network Turbo renewal worker synchronously in the request handler. Used for integration tests and operational diagnostics; the scheduler normally invokes this worker on an hourly cron. Request body is ignored. The trigger is recorded in the audit trail as `meta.background-job.run` against the calling admin, including runs that fail.

Requires features: dns_filtering.network_turbo.admin_update

**Tags:** DNS Filtering

**Requires authentication.**

**Features:** dns_filtering.network_turbo.admin_update

### Responses

**200** – Worker completed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**500** – Worker failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/dns_filtering/admin/workers/charge-network-turbo" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/dns_filtering/families/{id}/child-safety`

Get the family's children safety settings

Any family member may read. Returns the two DNS-related toggles plus a derived protectionStatus. When the family has no child members, both toggles are false and protectionStatus is "none".

Requires features: dns_filtering.safety.view

**Tags:** DNS Filtering

**Requires authentication.**

**Features:** dns_filtering.safety.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Family children safety settings

Content-Type: `application/json`

```json
{
  "contentFilterEnabled": true,
  "safeWebEnabled": true,
  "protectionStatus": "fully"
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dns_filtering/families/00000000-0000-4000-8000-000000000000/child-safety" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/dns_filtering/families/{id}/child-safety`

Update the family's children safety settings

Admin-only. Updates contentFilterEnabled and/or safeWebEnabled for all child members. Emits dns_filtering.safety.updated.

Requires features: dns_filtering.safety.update

**Tags:** DNS Filtering

**Requires authentication.**

**Features:** dns_filtering.safety.update

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Updated children safety settings

Content-Type: `application/json`

```json
{
  "contentFilterEnabled": true,
  "safeWebEnabled": true,
  "protectionStatus": "fully"
}
```

**400** – Invalid params or body

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Family has no child members to apply settings to

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/dns_filtering/families/00000000-0000-4000-8000-000000000000/child-safety" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## PUT `/dns_filtering/families/{id}/members/{memberId}/network-turbo`

Enable or disable Network Turbo on behalf of a family member

Family admin only. Enabling charges the admin's standard wallet upfront for the current cycle and writes a `fundedBy='family-admin'` entitlement for the member. The amount charged is the admin's localized price — a per-currency override (its EUR equivalent computed fresh, EUR floats) or the EUR default. If the member is currently self-paying (state='active' personal row), that row is frozen with `bankedUntil` capturing the unused prepaid time and `pausedAt` set to now — preserving the member's banked prepaid days for handoff when the admin record ends. Disabling defers: it flips auto-renewal off but keeps the admin-funded record active through its paid cycle (no refund); the renewal worker performs the handoff to the frozen personal sibling when the cycle elapses. The response carries the member's status plus `priceEuroCents` and (for non-EUR admins) `displayPrice` — the price the admin pays to fund the member.

Requires features: dns_filtering.network_turbo.admin_update

**Tags:** DNS Filtering

**Requires authentication.**

**Features:** dns_filtering.network_turbo.admin_update

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| memberId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "enabled": true
}
```

### Responses

**200** – Updated Network Turbo status for the member, with the admin's localized funding price

Content-Type: `application/json`

```json
{
  "enabled": true,
  "fundedBy": null,
  "paidUntil": null,
  "autoRenewalEnabled": true,
  "self": {
    "state": "string",
    "paidUntil": null,
    "bankedUntil": null,
    "pausedAt": null,
    "autoRenewalEnabled": true
  },
  "familyAdmin": {
    "state": "string",
    "paidUntil": null,
    "bankedUntil": null,
    "pausedAt": null,
    "autoRenewalEnabled": true
  },
  "priceEuroCents": 1
}
```

**400** – Invalid params/body, or admin tried to fund themselves (cannot_admin_fund_self)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**402** – Insufficient admin wallet balance

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family or member not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – A different family admin currently funds Network Turbo for this member (funded_by_different_admin)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/dns_filtering/families/00000000-0000-4000-8000-000000000000/members/00000000-0000-4000-8000-000000000000/network-turbo" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"enabled\": true
}"
```

## GET `/dns_filtering/users/me/dns-config`

Get the resolved DNS configuration for the current user

Returns the DNS provider, DoH URL, fallback IPs, and the reasons filtering is active. The mobile app calls this on launch and when it receives a dns_config_changed silent push, then applies the returned configuration to the OS DNS settings. Children's Safety inputs only apply when the caller's family role is 'child'; otherwise they are ignored.

**Tags:** DNS Filtering

**Requires authentication.**

### Responses

**200** – Resolved DNS configuration

Content-Type: `application/json`

```json
{
  "dnsMode": "default",
  "dohUrl": "https://example.com/resource",
  "dnsIps": [
    "string"
  ],
  "provider": "cloudflare-family",
  "reason": [
    "string"
  ],
  "enforcedBy": "self",
  "lastUpdatedAt": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dns_filtering/users/me/dns-config" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/dns_filtering/users/me/network-turbo`

Get the current user's Network Turbo status

Returns the caller's Network Turbo status as a per-source breakdown plus effective top-level fields. The `self` object reflects the caller's personally-funded record; the `familyAdmin` object reflects any family-admin-funded record covering the caller. Each source has `state` ('active', 'frozen', or 'disabled'), `paidUntil`, `bankedUntil`, `pausedAt`, and `autoRenewalEnabled`. `state='frozen'` means the user has banked prepaid time on this source while the sibling source is currently paying. Top-level `enabled`, `fundedBy`, `paidUntil`, and `autoRenewalEnabled` come from the currently paying row (the `active` row, or the `frozen` row as a transient fallback). Users with no Network Turbo rows return defaults with both sources disabled. `priceEuroCents` is the monthly price charged to the caller's wallet (an override currency floats with FX, so it is the live EUR equivalent); `displayPrice` is the localized monthly price (per-currency override, else the charm-rounded EUR default) and is omitted for EUR/null-currency users.

Requires features: dns_filtering.network_turbo.view

**Tags:** DNS Filtering

**Requires authentication.**

**Features:** dns_filtering.network_turbo.view

### Responses

**200** – Network Turbo status

Content-Type: `application/json`

```json
{
  "enabled": true,
  "fundedBy": null,
  "paidUntil": null,
  "autoRenewalEnabled": true,
  "self": {
    "state": "string",
    "paidUntil": null,
    "bankedUntil": null,
    "pausedAt": null,
    "autoRenewalEnabled": true
  },
  "familyAdmin": {
    "state": "string",
    "paidUntil": null,
    "bankedUntil": null,
    "pausedAt": null,
    "autoRenewalEnabled": true
  },
  "priceEuroCents": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/dns_filtering/users/me/network-turbo" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/dns_filtering/users/me/network-turbo`

Enable or disable Network Turbo for the current user

Self-activation for Network Turbo. Enabling from a disabled state charges the user's wallet upfront for the current calendar month, stamps fundedBy='self', and turns auto-renewal on. Strictly prepaid: returns 402 if the wallet balance is below the configured price. The amount charged is the caller's localized price — a per-currency override (its EUR equivalent computed fresh, EUR floats) or the EUR default. Disabling is deferred: it flips auto-renewal off but keeps Turbo on until the current paid period ends — no wallet refund. Re-enabling within an already-paid cycle simply flips auto-renewal back on without charging. Enabling is rejected with 409 conflict_admin_funded while a family admin is currently paying for Turbo on this user. The response includes `priceEuroCents` and (for non-EUR users) `displayPrice`.

Requires features: dns_filtering.network_turbo.update

**Tags:** DNS Filtering

**Requires authentication.**

**Features:** dns_filtering.network_turbo.update

### Request Body

Content-Type: `application/json`

```json
{
  "enabled": true
}
```

### Responses

**200** – Updated Network Turbo status

Content-Type: `application/json`

```json
{
  "enabled": true,
  "fundedBy": null,
  "paidUntil": null,
  "autoRenewalEnabled": true,
  "self": {
    "state": "string",
    "paidUntil": null,
    "bankedUntil": null,
    "pausedAt": null,
    "autoRenewalEnabled": true
  },
  "familyAdmin": {
    "state": "string",
    "paidUntil": null,
    "bankedUntil": null,
    "pausedAt": null,
    "autoRenewalEnabled": true
  },
  "priceEuroCents": 1
}
```

**400** – Invalid body

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**402** – Insufficient wallet balance

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Personal Turbo cannot be enabled while a family admin is currently paying for it

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/dns_filtering/users/me/network-turbo" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"enabled\": true
}"
```

## DELETE `/entities/definitions`

Soft delete custom field definition

Marks the specified definition inactive and tombstones it for the current scope.

Requires features: entities.definitions.manage

**Tags:** Entities

**Requires authentication.**

**Features:** entities.definitions.manage

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "key": "string"
}
```

### Responses

**200** – Definition deleted

Content-Type: `application/json`

```json
{
  "ok": true,
  "version": null
}
```

**400** – Missing entity id or key

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Definition not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/entities/definitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"key\": \"string\"
}"
```

## GET `/entities/definitions`

List active custom field definitions

Returns active custom field definitions for the supplied entity ids, respecting tenant scope and tombstones.

**Tags:** Entities

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Optional |
| entityIds | query | any | Optional |
| fieldset | query | any | Optional |

### Responses

**200** – Definition list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "key": "string",
      "kind": "string",
      "label": "string",
      "entityId": "string"
    }
  ]
}
```

**400** – Missing entity id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/entities/definitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/entities/definitions`

Upsert custom field definition

Creates or updates a custom field definition for the current tenant/org scope.

Requires features: entities.definitions.manage

**Tags:** Entities

**Requires authentication.**

**Features:** entities.definitions.manage

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "key": "string",
  "kind": "text"
}
```

### Responses

**200** – Definition saved

Content-Type: `application/json`

```json
{
  "ok": true,
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "key": "string",
    "kind": "string",
    "configJson": {}
  }
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/entities/definitions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"key\": \"string\",
  \"kind\": \"text\"
}"
```

## POST `/entities/definitions.batch`

Save multiple custom field definitions

Creates or updates multiple definitions for a single entity in one transaction.

Requires features: entities.definitions.manage

**Tags:** Entities

**Requires authentication.**

**Features:** entities.definitions.manage

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "definitions": [
    {
      "key": "string",
      "kind": "text"
    }
  ]
}
```

### Responses

**200** – Definitions saved

Content-Type: `application/json`

```json
{
  "ok": true,
  "version": null
}
```

**400** – Validation error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Unexpected failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/entities/definitions.batch" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"definitions\": [
    {
      \"key\": \"string\",
      \"kind\": \"text\"
    }
  ]
}"
```

## GET `/entities/definitions.manage`

Get management snapshot

Returns scoped custom field definitions (including inactive tombstones) for administration interfaces.

Requires features: entities.definitions.manage

**Tags:** Entities

**Requires authentication.**

**Features:** entities.definitions.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Required |

### Responses

**200** – Scoped definitions and deleted keys

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "key": "string",
      "kind": "string",
      "configJson": null,
      "organizationId": null,
      "tenantId": null
    }
  ],
  "deletedKeys": [
    "string"
  ],
  "version": null
}
```

**400** – Missing entity id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/entities/definitions.manage?entityId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/entities/definitions.restore`

Restore definition

Reactivates a previously soft-deleted definition within the current tenant/org scope.

Requires features: entities.definitions.manage

**Tags:** Entities

**Requires authentication.**

**Features:** entities.definitions.manage

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "key": "string"
}
```

### Responses

**200** – Definition restored

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing entity id or key

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Definition not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/entities/definitions.restore" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"key\": \"string\"
}"
```

## GET `/entities/encryption`

Fetch encryption map

Returns the encrypted field map for the current tenant/organization scope.

Requires features: entities.definitions.manage

**Tags:** Entities

**Requires authentication.**

**Features:** entities.definitions.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Required |

### Responses

**200** – Map

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "fields": [
    {
      "field": "string",
      "hashField": null
    }
  ],
  "updatedAt": null
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/entities/encryption?entityId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/entities/encryption`

Upsert encryption map

Creates or updates the encryption map for the current tenant/organization scope. Enforces optimistic locking when the caller sends the expected version header.

Requires features: entities.definitions.manage

**Tags:** Entities

**Requires authentication.**

**Features:** entities.definitions.manage

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "tenantId": null,
  "organizationId": null,
  "fields": [
    {
      "field": "string",
      "hashField": null
    }
  ]
}
```

### Responses

**200** – Saved

Content-Type: `application/json`

```json
{
  "ok": true,
  "updatedAt": null
}
```

**409** – Optimistic-lock conflict (stale write)

Content-Type: `application/json`

```json
{
  "error": "string",
  "code": "string",
  "currentUpdatedAt": "string",
  "expectedUpdatedAt": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/entities/encryption" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"tenantId\": null,
  \"organizationId\": null,
  \"fields\": [
    {
      \"field\": \"string\",
      \"hashField\": null
    }
  ]
}"
```

## DELETE `/entities/entities`

Soft delete custom entity

Marks the specified custom entity inactive within the current scope.

Requires features: entities.definitions.manage

**Tags:** Entities

**Requires authentication.**

**Features:** entities.definitions.manage

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string"
}
```

### Responses

**200** – Entity deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing entity id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Entity not found in scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/entities/entities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\"
}"
```

## GET `/entities/entities`

List available entities

Returns generated and custom entities scoped to the caller with field counts per entity.

**Tags:** Entities

**Requires authentication.**

### Responses

**200** – List of entities

Content-Type: `application/json`

```json
{
  "items": [
    {
      "entityId": "string",
      "source": "code",
      "label": "string",
      "count": 1
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/entities/entities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/entities/entities`

Upsert custom entity

Creates or updates a tenant/org scoped custom entity definition.

Requires features: entities.definitions.manage

**Tags:** Entities

**Requires authentication.**

**Features:** entities.definitions.manage

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "label": "string",
  "description": null,
  "showInSidebar": false
}
```

### Responses

**200** – Entity saved

Content-Type: `application/json`

```json
{
  "ok": true,
  "item": {
    "id": "00000000-0000-4000-8000-000000000000",
    "entityId": "string",
    "label": "string"
  }
}
```

**400** – Validation error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/entities/entities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"label\": \"string\",
  \"description\": null,
  \"showInSidebar\": false
}"
```

## DELETE `/entities/records`

Delete record

Soft deletes the specified record within the current tenant/org scope.

Requires features: entities.records.manage

**Tags:** Entities

**Requires authentication.**

**Features:** entities.records.manage

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "recordId": "string"
}
```

### Responses

**200** – Record deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing entity id or record id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Record not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Unexpected failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/entities/records" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"recordId\": \"string\"
}"
```

## GET `/entities/records`

List records

Returns paginated records for the supplied entity. Supports custom field filters, exports, and soft-delete toggles.

Requires features: entities.records.view

**Tags:** Entities

**Requires authentication.**

**Features:** entities.records.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Required |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| search | query | any | Optional |
| searchFields | query | any | Optional |
| withDeleted | query | any | Optional |
| format | query | any | Optional |
| exportScope | query | any | Optional |
| export_scope | query | any | Optional |
| all | query | any | Optional |
| full | query | any | Optional |

### Responses

**200** – Paginated records

Content-Type: `application/json`

```json
{
  "items": [
    {}
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**400** – Missing entity id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Unexpected failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/entities/records?entityId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/entities/records`

Create record

Creates a record for the given entity. When `recordId` is omitted or not a UUID the data engine will generate one automatically.

Requires features: entities.records.manage

**Tags:** Entities

**Requires authentication.**

**Features:** entities.records.manage

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "values": {}
}
```

### Responses

**200** – Record created

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Unexpected failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/entities/records" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"values\": {}
}"
```

## PUT `/entities/records`

Update record

Updates an existing record. If the provided recordId is not a UUID the record will be created instead to support optimistic flows.

Requires features: entities.records.manage

**Tags:** Entities

**Requires authentication.**

**Features:** entities.records.manage

### Request Body

Content-Type: `application/json`

```json
{
  "entityId": "string",
  "recordId": "string",
  "values": {}
}
```

### Responses

**200** – Record updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Validation failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Unexpected failure

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/entities/records" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityId\": \"string\",
  \"recordId\": \"string\",
  \"values\": {}
}"
```

## GET `/entities/relations/options`

List relation options

Returns up to 200 option entries for populating relation dropdowns, automatically resolving label fields when omitted.

Requires features: entities.definitions.view

**Tags:** Entities

**Requires authentication.**

**Features:** entities.definitions.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Required |
| labelField | query | any | Optional |
| q | query | any | Optional |
| ids | query | any | Optional |
| routeContextFields | query | any | Optional |

### Responses

**200** – Option list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "value": "string",
      "label": "string"
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/entities/relations/options?entityId=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/entities/sidebar-entities`

Get sidebar entities

Returns custom entities flagged with `showInSidebar` for the current tenant/org scope.

**Tags:** Entities

**Requires authentication.**

### Responses

**200** – Sidebar entities for navigation

Content-Type: `application/json`

```json
{
  "items": [
    {
      "entityId": "string",
      "label": "string",
      "href": "string"
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/entities/sidebar-entities" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/esims`

Create eSIM for authenticated consumer

Creates a new eSIM for the authenticated consumer. Provisions via 1Global.

Requires features: esims.create

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.create

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**201** – eSIM created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "pending"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**402** – Insufficient wallet balance

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Consumer profile not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Consumer already has an eSIM

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Home country code not set on consumer profile

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**502** – 1Global API error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## GET `/esims/{id}`

Get eSIM details

Returns full eSIM details including activation info, QR code, and universal install links. `installLinks.ios` works on iOS 17.4+; `installLinks.android` works on Android 10+. Both are null until 1Global has provided both `matching_id` and `smdp_address`. dataLeftBytes is the wallet balance converted to data at the eSIM current-country price (uncapped); usedBytes is data used since the last top-up summed across addons; totalAllowanceBytes = dataLeftBytes + usedBytes; wallets breaks it down per wallet type plus a `total` row. Deprecated: `totalBytes` and `dataLimitBytes` mirror `totalAllowanceBytes` (kept for older app builds).

Requires features: esims.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – eSIM details

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "iccid": null,
  "qrCodeUrl": null,
  "installLinks": {
    "ios": null,
    "android": null
  },
  "activationCode": null,
  "matchingId": null,
  "status": "pending",
  "phoneNumber": null,
  "countryCode": null,
  "usedBytes": 1,
  "dataLeftBytes": null,
  "totalAllowanceBytes": null,
  "usagePercent": null,
  "wallets": [
    {
      "type": "standard",
      "balanceEuroCents": 1,
      "dataLeftBytes": null
    }
  ],
  "totalBytes": 1,
  "dataLimitBytes": null,
  "expiresAt": null,
  "disableEsimInHomeZone": true,
  "isThrottled": true,
  "isInstalled": true
}
```

**400** – Invalid ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/esims/{id}`

Rename eSIM

Renames the consumer's own eSIM. Only the eSIM owner can rename it. The new name is reindexed for search.

Requires features: esims.rename

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.rename

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "name": "string"
}
```

### Responses

**200** – eSIM renamed

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "name": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/esims/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\"
}"
```

## POST `/esims/{id}/country`

Set the eSIM's current country and rebuild the addon stack

Updates Esim.currentCountryCode (and lastCountryAt) for the authenticated user's eSIM, then runs syncDataLimit so the addon stack is rebuilt for the new country's pricing. Only the eSIM owner can call this endpoint.

Requires features: esims.set-country

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.set-country

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "countryCode": "string"
}
```

### Responses

**200** – Country updated

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "countryCode": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found or not owned by the caller

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Country is not in the pricing table

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/00000000-0000-4000-8000-000000000000/country" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"countryCode\": \"string\"
}"
```

## PUT `/esims/{id}/home-zone`

Enable/disable home zone for eSIM

Toggles the home zone setting for the consumer's eSIM. Only the eSIM owner can update this. After persisting the setting, the addon stack is re-synced via 1Global to match it: cancelled when the eSIM is in the consumer's home country with the setting enabled, re-provisioned to match the wallet balance otherwise.

Requires features: esims.home-zone

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.home-zone

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "disableEsimInHomeZone": true
}
```

### Responses

**200** – Home zone updated

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "disableEsimInHomeZone": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/esims/00000000-0000-4000-8000-000000000000/home-zone" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"disableEsimInHomeZone\": true
}"
```

## GET `/esims/{id}/usage`

Get eSIM usage

Triggers a fresh 1Global usage sync, then returns wallet-derived usage with a 7-day timeline for charts. dataLeftBytes is the wallet balance converted to data at the eSIM current-country price (uncapped); usedBytes is data used since the last top-up summed across addons; totalAllowanceBytes = dataLeftBytes + usedBytes; usagePercent = usedBytes / totalAllowanceBytes; wallets breaks it down per wallet type plus a `total` row. Deprecated: `totalBytes` and `dataLimitBytes` mirror `totalAllowanceBytes` (kept for older app builds).

Requires features: esims.usage

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.usage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – eSIM usage data

Content-Type: `application/json`

```json
{
  "usedBytes": 1,
  "dataLeftBytes": null,
  "totalAllowanceBytes": null,
  "usagePercent": null,
  "wallets": [
    {
      "type": "standard",
      "balanceEuroCents": 1,
      "dataLeftBytes": null
    }
  ],
  "totalBytes": 1,
  "dataLimitBytes": null,
  "timeline": [
    {
      "date": "string",
      "usageBytes": 1,
      "totalBytes": 1
    }
  ]
}
```

**400** – Invalid ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/00000000-0000-4000-8000-000000000000/usage" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin`

List eSIMs

Returns eSIMs with pagination, search, and sorting. The search query matches (partial, case-insensitive) the owner email, eSIM name, ICCID and matching id (via the encrypted search token index), plus the 1Global order id, subscription id, subscriber id and account id (via direct lookup). When the query is a UUID it also matches the eSIM id and consumer profile id. Pass id query param to get a single eSIM. Each item includes universal `installLinks`: `installLinks.ios` requires iOS 17.4+ and `installLinks.android` requires Android 10+; both are null until 1Global has provided both `matching_id` and `smdp_address`.

Requires features: esims.admin.list

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| search | query | any | Optional |
| id | query | any | Optional |
| consumerId | query | any | Optional |

### Responses

**200** – eSIM list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "status": "pending",
      "iccid": null,
      "consumerProfileId": "00000000-0000-4000-8000-000000000000",
      "consumerEmail": null,
      "qrCodeUrl": null,
      "installLinks": {
        "ios": null,
        "android": null
      },
      "oneGlobalOrderId": null,
      "oneGlobalSubscriptionId": null,
      "isThrottled": true,
      "isInstalled": true,
      "createdAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin?page=1&pageSize=50&sortField=createdAt&sortDir=desc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/esims/admin`

Update eSIM

Updates an eSIM name.

Requires features: esims.admin.update

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.update

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "name": "string"
}
```

### Responses

**200** – eSIM updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/esims/admin" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"name\": \"string\"
}"
```

## POST `/esims/admin/__test__/age-snapshots`

Backdate usage snapshot created_at

Test-mode admin utility used by integration tests to backdate snapshot timestamps so the hourly worker re-processes them. Returns 404 unless ONE_GLOBAL_TEST_MODE=true.

Requires features: esims.admin.update

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.update

### Request Body

Content-Type: `application/json`

```json
{
  "esimId": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Snapshots aged

Content-Type: `application/json`

```json
{
  "ok": true,
  "updated": 1
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not found (test mode disabled)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/__test__/age-snapshots" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"esimId\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## POST `/esims/admin/__test__/one-global-spent`

Set or clear 1Global test-mode spent override

Test-mode admin utility used by integration tests to deterministically seed the bytes 1Global reports as spent for a given subscriptionId. Returns 404 unless ONE_GLOBAL_TEST_MODE=true.

Requires features: esims.admin.update

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.update

### Request Body

Content-Type: `application/json`

```json
{
  "subscriptionId": "string",
  "spentBytes": 1
}
```

### Responses

**200** – Override applied

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not found (test mode disabled)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/__test__/one-global-spent" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"subscriptionId\": \"string\",
  \"spentBytes\": 1
}"
```

## DELETE `/esims/admin/__test__/refunds-reports`

Clean up seeded payments and reports

Deletes the given payments/reports. Returns 404 unless ONE_GLOBAL_TEST_MODE=true.

Requires features: esims.refunds-report.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.refunds-report.view

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Deleted

Content-Type: `application/json`

```json
{}
```

**404** – Not found (test mode disabled)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/esims/admin/__test__/refunds-reports" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/esims/admin/__test__/refunds-reports`

Drive refunds-report flows for integration tests

Seeds refunded payments, generates/escalates a refunds report with an injected clock, or backdates a report. Returns 404 unless ONE_GLOBAL_TEST_MODE=true.

Requires features: esims.refunds-report.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.refunds-report.view

### Request Body

Content-Type: `application/json`

```json
{
  "action": "seed-payment",
  "amountEuroCents": 1,
  "amountRefundedEuroCents": 1,
  "refundedAt": "string",
  "status": "refunded"
}
```

### Responses

**200** – Action result

Content-Type: `application/json`

```json
{}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not found (test mode disabled)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/__test__/refunds-reports" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"action\": \"seed-payment\",
  \"amountEuroCents\": 1,
  \"amountRefundedEuroCents\": 1,
  \"refundedAt\": \"string\",
  \"status\": \"refunded\"
}"
```

## POST `/esims/admin/__test__/remittance-object`

Seed a stored remittance .xlsx (test only)

Writes the given base64 .xlsx to object storage under the period key, simulating a completed signed-URL upload. Returns 404 unless ONE_GLOBAL_TEST_MODE=true.

Requires features: esims.remittance.import

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.remittance.import

### Request Body

Content-Type: `application/json`

```json
{
  "period": "string",
  "fileBase64": "string"
}
```

### Responses

**200** – Object stored

Content-Type: `application/json`

```json
{
  "storageKey": "string"
}
```

**404** – Not in test mode

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/__test__/remittance-object" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"period\": \"string\",
  \"fileBase64\": \"string\"
}"
```

## GET `/esims/admin/__test__/snapshots`

List usage snapshots for an eSIM

Test-mode admin utility used by integration tests to inspect snapshot state. Returns 404 unless ONE_GLOBAL_TEST_MODE=true.

Requires features: esims.admin.list

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| esimId | query | any | Required |

### Responses

**200** – Snapshot list

Content-Type: `application/json`

```json
{
  "snapshots": [
    {
      "id": "string",
      "esimId": "string",
      "initialBytes": "string",
      "spentBytes": "string",
      "remainingBytes": "string",
      "countryCode": null,
      "source": "string",
      "billedAt": null,
      "previousUsageSnapshotId": null,
      "standardDebitTransactionId": null,
      "bonusDebitTransactionId": null,
      "createdAt": "string"
    }
  ]
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not found (test mode disabled)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/__test__/snapshots?esimId=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/{id}`

Get eSIM detail

Returns full eSIM state including the SM-DP+ address and universal install links (`installLinks.ios` requires iOS 17.4+; `installLinks.android` requires Android 10+; both null until 1Global has provided both `matching_id` and `smdp_address`), the eSIM consumer, the billing user (which may differ from the consumer for family plans) along with their wallets, auto-refill state, and spending cap, the addon stack summary, data products, and recent usage snapshots. Pass `includeDropped=true` to include data products that 1Global has stopped reporting (default hides them).

Requires features: esims.admin.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| includeDropped | query | any | Optional |

### Responses

**200** – eSIM detail

Content-Type: `application/json`

```json
{
  "esim": {
    "id": "00000000-0000-4000-8000-000000000000",
    "name": "string",
    "status": "pending",
    "iccid": null,
    "countryOfIssue": null,
    "matchingId": null,
    "smdpAddress": null,
    "qrCodeUrl": null,
    "installLinks": {
      "ios": null,
      "android": null
    },
    "disableEsimInHomeZone": true,
    "isThrottled": true,
    "isInstalled": true,
    "currentCountryCode": null,
    "lastLifecycleAt": null,
    "lastUsageAt": null,
    "lastCountryAt": null,
    "lastInstallationAt": null,
    "oneGlobalOrderId": null,
    "oneGlobalSubscriptionId": null,
    "consumerProfileId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string"
  },
  "consumer": {
    "id": "00000000-0000-4000-8000-000000000000",
    "userId": "00000000-0000-4000-8000-000000000000",
    "email": null,
    "fullName": null,
    "firstName": null,
    "lastName": null,
    "phone": null,
    "languageCode": null,
    "displayCurrency": null,
    "homeCountryCode": null,
    "travelProfile": null,
    "addressLine1": null,
    "addressLine2": null,
    "addressCity": null,
    "addressState": null,
    "addressCountry": null,
    "addressPostalCode": null,
    "birthDate": null
  },
  "billing": {
    "userId": "00000000-0000-4000-8000-000000000000",
    "email": null,
    "fullName": null,
    "isSelfBilled": true,
    "billingUserMissing": true,
    "consumerProfileId": null,
    "spendingCapEuroCents": null,
    "autoRefill": {
      "enabled": true,
      "amountEuroCents": null,
      "thresholdEuroCents": null,
      "monthlyCapEuroCents": null,
      "pausedAt": null,
      "pausedReason": null
    },
    "wallets": {
      "standard": null,
      "bonus": null
    }
  },
  "oneGlobalAccountId": null,
  "oneGlobalSubscriberId": null,
  "addonStackSummary": {
    "activeCount": 1,
    "pendingCount": 1,
    "toBeOrderedCount": 1,
    "depletedCount": 1,
    "cancelledCount": 1,
    "failedCount": 1,
    "expiredCount": 1,
    "terminatedCount": 1,
    "totalActiveBytes": 1,
    "totalLeftCapacityBytes": 1,
    "actualTotalAllowanceBytes": null,
    "actualLeftAllowanceBytes": null,
    "spentBytes": 1,
    "lastAddedAt": null
  },
  "dataProducts": [
    {
      "id": "string",
      "status": "pending",
      "dropped": true,
      "oneGlobalProductId": null,
      "oneGlobalOrderId": null,
      "oneGlobalOfferingId": null,
      "offeringName": null,
      "offeringCountries": null,
      "dataBytes": 1,
      "startedAt": null,
      "remainingBytes": null,
      "lastWebhookEventAt": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "provisioningProgress": null,
  "usageSnapshots": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "source": "api",
      "countryCode": null,
      "initialBytes": 1,
      "spentBytes": 1,
      "remainingBytes": 1,
      "billedAt": null,
      "createdAt": "string"
    }
  ]
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/00000000-0000-4000-8000-000000000000?includeDropped=false" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/{id}/calls`

List 1Global API calls related to an eSIM

Returns paginated 1Global API calls (outbound + inbound webhooks) correlated to a specific eSIM via direct esim_id, order, subscription, ICCID, matching id, or pre-creation account/subscriber events on the consumer profile. When `oneGlobalOrderId` or `oneGlobalProductId` query params are supplied, results are instead scoped to a single data product (matching either identifier) instead of the eSIM-wide correlation. Returns 404 if no data product on this eSIM matches the supplied identifiers. The `eventTypes` query param is repeatable and filters rows to `eventType IN (...)`. The response also returns `availableEventTypes` — the distinct, alphabetically sorted set of event types observed within the scope (ignoring the active `eventTypes` filter) so callers can render a stable filter dropdown.

Requires features: esims.admin.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| direction | query | any | Optional |
| handlingStatus | query | any | Optional |
| eventTypes | query | any | Optional |
| from | query | any | Optional |
| to | query | any | Optional |
| oneGlobalOrderId | query | any | Optional |
| oneGlobalProductId | query | any | Optional |

### Responses

**200** – 1Global API calls

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "direction": "outbound",
      "handlingStatus": null,
      "method": "string",
      "endpoint": "string",
      "eventType": null,
      "statusCode": null,
      "durationMs": null,
      "idempotencyKey": null,
      "externalEventId": null,
      "errorMessage": null,
      "esimId": null,
      "consumerProfileId": null,
      "oneGlobalOrderId": null,
      "oneGlobalSubscriptionId": null,
      "oneGlobalProductId": null,
      "trigger": null,
      "triggers": null,
      "createdAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1,
  "availableEventTypes": [
    "string"
  ]
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/00000000-0000-4000-8000-000000000000/calls?page=1&pageSize=25" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/{id}/calls/{callId}`

Get a single 1Global API call belonging to an eSIM

Returns the full payload (request/response headers and bodies) for a single OneGlobalApiCall that correlates to the specified eSIM via direct esim_id, order, subscription, ICCID, matching id, or a pre-creation account/subscriber event on the consumer profile. Returns 404 if the call does not exist or is not correlated to this eSIM.

Requires features: esims.admin.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| callId | path | any | Required |

### Responses

**200** – 1Global API call detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "direction": "outbound",
  "handlingStatus": null,
  "method": "string",
  "endpoint": "string",
  "eventType": null,
  "statusCode": null,
  "durationMs": null,
  "idempotencyKey": null,
  "externalEventId": null,
  "errorMessage": null,
  "esimId": null,
  "consumerProfileId": null,
  "oneGlobalOrderId": null,
  "oneGlobalSubscriptionId": null,
  "oneGlobalProductId": null,
  "trigger": null,
  "triggers": null,
  "createdAt": "string",
  "requestHeaders": null,
  "requestBody": null,
  "responseHeaders": null,
  "responseBody": null
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM or call not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/00000000-0000-4000-8000-000000000000/calls/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/esims/admin/{id}/country`

Manually set eSIM's current country and rebuild the addon stack

Updates Esim.currentCountryCode (and lastCountryAt) for the eSIM, then runs syncDataLimit so the addon stack is rebuilt for the new country's pricing. Recorded in the action log AND surfaced in the eSIM's 1Global activity log as an admin_action entry.

Requires features: esims.admin.set-country

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.set-country

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "countryCode": "string"
}
```

### Responses

**200** – Country updated

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "countryCode": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Country is not in the pricing table

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/00000000-0000-4000-8000-000000000000/country" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"countryCode\": \"string\"
}"
```

## POST `/esims/admin/{id}/country-recheck`

Send a country-recheck push (admin/QA tool)

Fires a country-recheck push at the eSIM owner on demand. `variant: 'silent'` sends a data-only push that wakes the app to silently re-detect location; `variant: 'visible'` sends a user-visible push asking the traveller to open the app. Both bypass per-user opt-out and frequency caps (the types are declared nonOptOut). Exercises the same send path the sensor-addon detection flow will use. For the silent variant the response carries per-device delivery counts; for the visible variant it carries the created in-app notification id.

Requires features: esims.admin.country-recheck

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.country-recheck

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "variant": "silent"
}
```

### Responses

**200** – Country-recheck push dispatched

Content-Type: `application/json`

```json
{
  "ok": true,
  "variant": "silent",
  "userId": "00000000-0000-4000-8000-000000000000",
  "notificationId": null,
  "delivery": null
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/00000000-0000-4000-8000-000000000000/country-recheck" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"variant\": \"silent\"
}"
```

## POST `/esims/admin/{id}/data-limit`

Manually set eSIM's data limit by triggering an addon-stack rebuild

Calls syncDataLimit with an admin-supplied target (in bytes) instead of the wallet-derived target. The result lives in the addon stack: 1Global products are purchased and/or cancelled to match. The action is recorded in the action log AND in the eSIM's 1Global activity log as an admin_action entry.

Requires features: esims.admin.set-data-limit

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.set-data-limit

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "targetBytes": 1
}
```

### Responses

**200** – Data limit synced

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "targetBytes": 1
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – eSIM has no 1Global subscription or current country

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/00000000-0000-4000-8000-000000000000/data-limit" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"targetBytes\": 1
}"
```

## POST `/esims/admin/{id}/fake-usage`

Simulate data consumption on an eSIM (admin/QA tool)

Simulates usage in a chosen country by emitting the same internal events the 1Global threshold/depleted webhooks emit, so the result is indistinguishable from real usage. Targets the data product real usage in that country would consume: the oldest active addon (first in the depletion order) whose offering covers the country. If no such product exists, nothing happens and the response carries `applied: false` with a `reason`. Defaults the country to the eSIM's current country when omitted. Builds the synthetic balance from local state (latest usage snapshot, falling back to the product's allowance) and never reads from 1Global. When usage IS applied the downstream effects are real: a real wallet debit for the consumed bytes, cap-threshold notifications, and — when the amount consumes the product's full remaining balance — a real `product.depleted` flow that marks the product depleted and provisions the next addon via live 1Global calls. The usage snapshot is created asynchronously by the subscriber, so it is not returned here.

Requires features: esims.admin.fake-usage

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.fake-usage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "amountGb": 1
}
```

### Responses

**200** – Fake usage event emitted (applied: true), or a no-op when no covering product exists (applied: false)

Content-Type: `application/json`

```json
{
  "ok": true,
  "applied": true,
  "reason": null,
  "countryCode": null,
  "productId": null,
  "amountGb": 1,
  "depleted": true,
  "balance": null
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/00000000-0000-4000-8000-000000000000/fake-usage" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"amountGb\": 1
}"
```

## PUT `/esims/admin/{id}/home-zone`

Admin override of an eSIM's home zone status

Toggles Esim.disableEsimInHomeZone for the eSIM, regardless of consumer ownership. After persisting the setting, the addon stack is re-synced via 1Global in both directions: cancelled when the eSIM is in the consumer's home country with the setting enabled, re-provisioned to match the wallet balance otherwise. Recorded as an admin_action entry in the eSIM's 1Global activity log.

Requires features: esims.admin.home-zone

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.home-zone

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "disableEsimInHomeZone": true
}
```

### Responses

**200** – Home zone updated

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "disableEsimInHomeZone": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/esims/admin/00000000-0000-4000-8000-000000000000/home-zone" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"disableEsimInHomeZone\": true
}"
```

## POST `/esims/admin/{id}/refresh`

Refresh eSIM data from 1Global

Pulls live subscription state and products from 1Global, reconciles eSIM fields (status, iccid, matching id, qr code, installed flag), upserts esim_data_products rows from the live 1Global products list, and creates a usage snapshot when a data balance is returned and the reading differs from the previous snapshot. The snapshot event triggers wallet billing and data-limit recalculation through existing subscribers. snapshotId is null when no data balance is available or when the reading matches the previous snapshot.

Requires features: esims.admin.refresh

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.refresh

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Refresh completed

Content-Type: `application/json`

```json
{
  "esimId": "00000000-0000-4000-8000-000000000000",
  "snapshotId": null
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – eSIM has no 1Global subscription

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**502** – 1Global API error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/00000000-0000-4000-8000-000000000000/refresh" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/{id}/usage-snapshots/{snapshotId}`

Get a usage snapshot and everything linked to it

Returns a single usage snapshot belonging to the eSIM along with the wallet debit transactions it created (standard + bonus), the payments that funded those debits, the previous snapshot in the billing chain, the data product, and the originating 1Global API call(s). Calls are resolved precisely via the snapshot's one_global_correlation_id (the same correlation id stamped on the OneGlobalApiCall that produced the reading); snapshots recorded before this link existed return an empty originatingCalls list. Returns 404 when the snapshot does not exist or does not belong to this eSIM.

Requires features: esims.admin.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| snapshotId | path | any | Required |

### Responses

**200** – Usage snapshot detail

Content-Type: `application/json`

```json
{
  "snapshot": {
    "id": "00000000-0000-4000-8000-000000000000",
    "esimId": "00000000-0000-4000-8000-000000000000",
    "oneGlobalProductId": null,
    "oneGlobalCorrelationId": null,
    "source": "api",
    "countryCodes": [
      "string"
    ],
    "countryCode": null,
    "initialBytes": 1,
    "spentBytes": 1,
    "deltaBytes": 1,
    "remainingBytes": 1,
    "billedEuroCents": null,
    "billingRemainderCents": "string",
    "previousUsageSnapshotId": null,
    "standardDebitTransactionId": null,
    "bonusDebitTransactionId": null,
    "billedAt": null,
    "createdAt": "string"
  },
  "walletDebits": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "role": "standard",
      "walletId": "00000000-0000-4000-8000-000000000000",
      "type": "string",
      "amountEuroCents": 1,
      "description": "string",
      "referenceType": "string",
      "referenceId": null,
      "createdAt": "string"
    }
  ],
  "payments": [
    {
      "paymentId": null,
      "contributedEuroCents": 1,
      "status": null,
      "paymentType": null,
      "amountEuroCents": null,
      "originalCurrency": null,
      "originalAmountMinorUnits": null,
      "platformPaymentId": null,
      "description": null,
      "createdAt": null
    }
  ],
  "previousSnapshot": null,
  "dataProduct": null,
  "originatingCalls": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "direction": "outbound",
      "handlingStatus": null,
      "method": "string",
      "endpoint": "string",
      "eventType": null,
      "statusCode": null,
      "durationMs": null,
      "idempotencyKey": null,
      "externalEventId": null,
      "errorMessage": null,
      "esimId": null,
      "consumerProfileId": null,
      "oneGlobalOrderId": null,
      "oneGlobalSubscriptionId": null,
      "oneGlobalProductId": null,
      "trigger": null,
      "triggers": null,
      "createdAt": "string"
    }
  ]
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM or snapshot not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/00000000-0000-4000-8000-000000000000/usage-snapshots/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/{id}/wallet-transactions`

List wallet transactions caused by this eSIM

Returns paginated wallet debits for data usage on this eSIM. Resolves the billing user (the consumer self-bills unless an EsimBillingConfig points elsewhere) and filters wallet transactions by that user's wallets and the eSIM's usage snapshot IDs.

Requires features: esims.admin.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – Wallet transactions

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "walletId": "00000000-0000-4000-8000-000000000000",
      "walletType": "string",
      "type": "string",
      "amountEuroCents": 1,
      "description": "string",
      "referenceType": "string",
      "referenceId": null,
      "createdAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – eSIM not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/00000000-0000-4000-8000-000000000000/wallet-transactions?page=1&pageSize=25" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/1global-status`

Get 1Global provider status (admin)

Fetches 1Global's public StatusPal summary plus 90 days of incident history and returns the overall status, open incidents, active and upcoming maintenances, and the service health tree with each service's per-day status, the incidents that impacted each day, and uptime ratio. Cached for 5 minutes.

Requires features: esims.admin.1global-status.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.1global-status.view

### Responses

**200** – 1Global status summary

Content-Type: `application/json`

```json
{
  "status": "operational",
  "pageName": "string",
  "pageUrl": "string",
  "timeZone": null,
  "incidents": [
    {
      "id": 1,
      "title": "string",
      "url": null,
      "severity": null,
      "startsAt": null,
      "endsAt": null,
      "affectsUptime": true,
      "latestUpdate": null
    }
  ],
  "maintenances": [
    {
      "id": 1,
      "title": "string",
      "url": null,
      "startsAt": null,
      "endsAt": null,
      "latestUpdate": null
    }
  ],
  "upcomingMaintenances": [
    {
      "id": 1,
      "title": "string",
      "url": null,
      "startsAt": null,
      "endsAt": null,
      "latestUpdate": null
    }
  ],
  "services": [
    {
      "id": 1,
      "name": "string",
      "status": "operational",
      "uptimeRatio": 1,
      "dailyStatus": [
        {
          "date": "string",
          "status": "operational",
          "incidents": [
            {
              "id": 1,
              "title": "string",
              "severity": "operational",
              "startsAt": null,
              "endsAt": null
            }
          ]
        }
      ],
      "children": []
    }
  ]
}
```

**502** – Failed to fetch the 1Global status page

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/1global-status" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/1global-status/incidents/{id}`

Get 1Global incident details (admin)

Fetches a single 1Global StatusPal incident by id, including its full update timeline, and returns the normalized incident detail. Cached for 5 minutes.

Requires features: esims.admin.1global-status.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.1global-status.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – 1Global incident detail

Content-Type: `application/json`

```json
{
  "id": 1,
  "title": "string",
  "url": null,
  "severity": null,
  "startsAt": null,
  "endsAt": null,
  "affectsUptime": true,
  "updates": [
    {
      "type": null,
      "description": "string",
      "updatedAt": null
    }
  ]
}
```

**400** – Invalid incident id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Incident not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**502** – Failed to fetch the 1Global incident

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/1global-status/incidents/string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/consumer/{consumerId}/calls`

List 1Global API calls related to a consumer

Returns paginated 1Global API calls (outbound calls, inbound webhooks, admin and system actions) correlated to a consumer via its consumer_profile_id or any eSIM owned by the consumer. Supports the same `direction`, `handlingStatus`, `from`/`to`, and repeatable `eventTypes` filters as the eSIM-scoped call log. Returns 404 when the consumer does not exist or belongs to another organization. `availableEventTypes` lists the distinct event types observed for the consumer's eSIMs plus the pre-eSIM account/subscriber creation events.

Requires features: esims.admin.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| consumerId | path | any | Required |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| direction | query | any | Optional |
| handlingStatus | query | any | Optional |
| eventTypes | query | any | Optional |
| from | query | any | Optional |
| to | query | any | Optional |
| oneGlobalOrderId | query | any | Optional |
| oneGlobalProductId | query | any | Optional |

### Responses

**200** – 1Global API calls

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "direction": "outbound",
      "handlingStatus": null,
      "method": "string",
      "endpoint": "string",
      "eventType": null,
      "statusCode": null,
      "durationMs": null,
      "idempotencyKey": null,
      "externalEventId": null,
      "errorMessage": null,
      "esimId": null,
      "consumerProfileId": null,
      "oneGlobalOrderId": null,
      "oneGlobalSubscriptionId": null,
      "oneGlobalProductId": null,
      "trigger": null,
      "triggers": null,
      "createdAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1,
  "availableEventTypes": [
    "string"
  ]
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Consumer not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/consumer/00000000-0000-4000-8000-000000000000/calls?page=1&pageSize=25" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/consumer/{consumerId}/calls/{callId}`

Get a single 1Global API call belonging to a consumer

Returns the full payload (request/response headers and bodies) for a single OneGlobalApiCall correlated to the consumer via its consumer_profile_id or any eSIM owned by the consumer. Returns 404 if the consumer does not exist, belongs to another organization, or the call is not correlated to the consumer.

Requires features: esims.admin.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| consumerId | path | any | Required |
| callId | path | any | Required |

### Responses

**200** – 1Global API call detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "direction": "outbound",
  "handlingStatus": null,
  "method": "string",
  "endpoint": "string",
  "eventType": null,
  "statusCode": null,
  "durationMs": null,
  "idempotencyKey": null,
  "externalEventId": null,
  "errorMessage": null,
  "esimId": null,
  "consumerProfileId": null,
  "oneGlobalOrderId": null,
  "oneGlobalSubscriptionId": null,
  "oneGlobalProductId": null,
  "trigger": null,
  "triggers": null,
  "createdAt": "string",
  "requestHeaders": null,
  "requestBody": null,
  "responseHeaders": null,
  "responseBody": null
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Consumer or call not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/consumer/00000000-0000-4000-8000-000000000000/calls/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/esims/admin/device-support`

Delete device model verdict

Soft-deletes a device model by `id` (query param). Action is logged and reversible via undo.

Requires features: esims.admin.device-support.delete

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.device-support.delete

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required |

### Responses

**200** – Device model deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Device model not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/esims/admin/device-support?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/device-support`

List device models (admin)

Returns the admin-editable device eSIM support table with pagination, search by model identifier, and an optional status filter.

Requires features: esims.admin.device-support.list

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.device-support.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| search | query | any | Optional |
| status | query | any | Optional |
| id | query | any | Optional |

### Responses

**200** – Device model list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "modelIdentifier": "string",
      "status": "supported",
      "reason": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/device-support?page=1&pageSize=50&sortField=modelIdentifier&sortDir=asc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/esims/admin/device-support`

Create device model verdict

Adds a model identifier with a certain supported/unsupported verdict. Action is logged and reversible via undo (soft-deletes the new row).

Requires features: esims.admin.device-support.create

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.device-support.create

### Request Body

Content-Type: `application/json`

```json
{
  "modelIdentifier": "string",
  "status": "supported",
  "reason": null
}
```

### Responses

**201** – Device model created

Content-Type: `application/json`

```json
{
  "ok": true,
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Device model already exists

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/device-support" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"modelIdentifier\": \"string\",
  \"status\": \"supported\",
  \"reason\": null
}"
```

## PUT `/esims/admin/device-support`

Update device model verdict

Updates the status and/or reason for a device model, identified by `id` in the body. Action is logged and reversible via undo.

Requires features: esims.admin.device-support.update

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.device-support.update

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "reason": null
}
```

### Responses

**200** – Device model updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Device model not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/esims/admin/device-support" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"reason\": null
}"
```

## POST `/esims/admin/generate`

Generate eSIM for consumer

Creates 1Global account/subscriber if needed, then places an activate_subscription order. Returns immediately with pending status; actual eSIM provisioning happens async via webhooks.

Requires features: esims.admin.generate

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.generate

### Request Body

Content-Type: `application/json`

```json
{
  "consumerId": "00000000-0000-4000-8000-000000000000",
  "name": "string"
}
```

### Responses

**201** – eSIM generation started

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "pending"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Consumer not found or not active

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**502** – 1Global API error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/generate" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"consumerId\": \"00000000-0000-4000-8000-000000000000\",
  \"name\": \"string\"
}"
```

## GET `/esims/admin/product-offerings`

List product offerings (admin)

Returns the cached 1Global product offerings catalog with pagination, search by name or 1Global offering id (and by the offering id when the query is a UUID), and optional status and coverage type (local/regional/global) filters.

Requires features: esims.admin.product-offerings.list

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.product-offerings.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| search | query | any | Optional |
| status | query | any | Optional |
| coverageType | query | any | Optional |

### Responses

**200** – Product offering list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "oneGlobalOfferingId": "string",
      "name": "string",
      "status": "string",
      "type": "string",
      "category": "string",
      "coverageType": "local",
      "dataBytes": 1,
      "countries": [
        "string"
      ],
      "countryAvailability": "all",
      "missingCountries": [
        "string"
      ],
      "validityUnit": null,
      "validityUnitCount": null,
      "syncedAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load product offerings

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/product-offerings?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/esims/admin/product-offerings/sync`

Trigger product offerings sync (admin)

Synchronously runs the 1Global product offerings sync for the authenticated tenant. Wraps the same command the hourly scheduled job invokes.

Requires features: esims.admin.product-offerings.sync

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.product-offerings.sync

### Responses

**200** – Sync completed

Content-Type: `application/json`

```json
{
  "ok": true,
  "upserted": 1,
  "skipped": 1,
  "deactivated": null
}
```

**400** – Invalid request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Sync failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/product-offerings/sync" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/refunds-reports`

List 1Global refunds reports

Returns the 1Global refunds reports (newest period and version first), optionally filtered by state. Multiple versions per period may exist when a report has been regenerated. Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.refunds-report.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.refunds-report.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| state | query | any | Optional |

### Responses

**200** – Refunds report list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "periodStart": "string",
      "periodEnd": "string",
      "version": 1,
      "source": "scheduled",
      "state": "pending_review",
      "sentMode": null,
      "sentAt": null,
      "reminderSentAt": null,
      "failureReason": null,
      "rowCount": 1,
      "generatedAt": "string",
      "createdAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/refunds-reports?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/esims/admin/refunds-reports`

Generate a manual 1Global refunds report for a date range

Generates a manual refunds report covering the inclusive [from, to] calendar-day range. Idempotent for the exact range: an existing pending_review or sent report is returned unchanged; otherwise a new version is created. Manual reports are never auto-sent by the escalation. Use the [id]/regenerate endpoint to force a new version. Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.refunds-report.generate

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.refunds-report.generate

### Request Body

Content-Type: `application/json`

```json
{
  "from": "string",
  "to": "string"
}
```

### Responses

**200** – Generation result

Content-Type: `application/json`

```json
{
  "reportId": "00000000-0000-4000-8000-000000000000",
  "generated": true,
  "state": "pending_review",
  "rowCount": 1,
  "periodLabel": "string",
  "version": 1
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Generation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/refunds-reports" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"from\": \"string\",
  \"to\": \"string\"
}"
```

## GET `/esims/admin/refunds-reports/{id}/download`

Download a 1Global refunds report (.xlsx)

Streams the stored .xlsx for the given refunds report. Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.refunds-report.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.refunds-report.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Refunds report .xlsx

Content-Type: `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Refunds report not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/refunds-reports/00000000-0000-4000-8000-000000000000/download" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/esims/admin/refunds-reports/{id}/regenerate`

Regenerate a 1Global refunds report as a new version

Generates a new manual version for the given report's date range, preserving prior versions. The new version is pending_review and, being manual, is never auto-sent by the escalation. Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.refunds-report.generate

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.refunds-report.generate

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – New version generated

Content-Type: `application/json`

```json
{
  "reportId": "00000000-0000-4000-8000-000000000000",
  "generated": true,
  "state": "pending_review",
  "rowCount": 1,
  "periodLabel": "string",
  "version": 1
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Refunds report not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Generation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/refunds-reports/00000000-0000-4000-8000-000000000000/regenerate" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/refunds-reports/{id}/rows`

List parsed rows of a refunds report worksheet

Re-parses the stored .xlsx and returns its refund rows (the exact worksheet sent to 1Global), paged. Each row is keyed by the report column headers with formatted string cell values. Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.refunds-report.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.refunds-report.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – Parsed refunds report rows

Content-Type: `application/json`

```json
{
  "items": [
    {
      "key": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid parameters or query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Refunds report not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – File not stored

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load refunds report rows

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/refunds-reports/00000000-0000-4000-8000-000000000000/rows?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/esims/admin/refunds-reports/{id}/send`

Send a 1Global refunds report to 1Global

Emails the stored .xlsx to 1Global and marks the report sent (manual). Sending an already-sent report is a no-op that returns the current state. Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.refunds-report.send

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.refunds-report.send

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Report sent (or already sent)

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "state": "pending_review"
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Refunds report not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Recipient not configured or email delivery failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/refunds-reports/00000000-0000-4000-8000-000000000000/send" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/esims/admin/remittance/import`

Confirm an uploaded 1Global remittance note and auto-detect columns

Call after the client has uploaded the .xlsx to the signed URL. Streams the stored workbook to detect columns (header rows + cover labels only, never buffering the whole file) and to hash its bytes. A byte-identical re-upload of a period returns the existing version unchanged (created=false); a differing file is stored as a new version (created=true), preserving prior versions. Returns pending when every needed column matched the default headers, or pending_mapping when some did not. A corrupt file is rejected (422) and the orphan object removed; a period with no uploaded file returns 409. Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.remittance.import

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.remittance.import

### Request Body

Content-Type: `application/json`

```json
{
  "period": "string"
}
```

### Responses

**200** – Remittance note ingested and columns detected

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "period": "string",
  "version": 1,
  "created": true,
  "status": "pending_mapping",
  "detected": {
    "subscribers": {
      "mapping": {
        "key": "string"
      },
      "fileColumns": [
        "string"
      ],
      "unmatched": [
        "string"
      ]
    },
    "activation": {
      "mapping": {
        "key": "string"
      },
      "fileColumns": [
        "string"
      ],
      "unmatched": [
        "string"
      ]
    },
    "cover": {
      "mapping": {
        "key": "string"
      },
      "fileColumns": [
        "string"
      ],
      "unmatched": [
        "string"
      ]
    }
  },
  "fileColumns": [
    "string"
  ],
  "unmatched": [
    "string"
  ]
}
```

**409** – No uploaded file for this period

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Invalid period or unreadable workbook

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Ingest failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/remittance/import" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"period\": \"string\"
}"
```

## POST `/esims/admin/remittance/import/upload-url`

Create a signed URL to upload a 1Global remittance note

Returns a short-lived signed PUT URL the client uploads the monthly .xlsx to directly (object storage), plus the storage key. The bytes never pass through the backend - confirm the upload via POST /api/esims/admin/remittance/import. Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.remittance.import

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.remittance.import

### Request Body

Content-Type: `application/json`

```json
{
  "period": "string"
}
```

### Responses

**200** – Signed upload URL

Content-Type: `application/json`

```json
{
  "uploadUrl": "string",
  "storageKey": "string",
  "period": "string"
}
```

**422** – Invalid period

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to create upload URL

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/remittance/import/upload-url" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"period\": \"string\"
}"
```

## GET `/esims/admin/remittance/imports`

List 1Global remittance imports

Returns the monthly 1Global remittance imports (newest period first, newest version first within a period), optionally filtered by status. A period can have multiple versions when a differing file was re-uploaded. Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.remittance.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.remittance.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| status | query | any | Optional |

### Responses

**200** – Remittance import list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "period": "string",
      "version": 1,
      "status": "pending_mapping",
      "uploadedByUserId": "string",
      "uploadedAt": "string",
      "rowCounts": null,
      "discrepancyCounts": null,
      "createdAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/remittance/imports?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/remittance/imports/{id}`

Get a 1Global remittance import

Returns a single remittance import with its resolved column mapping, row counts, discrepancy counts, cover settlement totals and error message. Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.remittance.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.remittance.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Remittance import detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "period": "string",
  "version": 1,
  "status": "pending_mapping",
  "columnMapping": null,
  "uploadedByUserId": "string",
  "uploadedAt": "string",
  "rowCounts": null,
  "discrepancyCounts": null,
  "coverTotals": null,
  "errorMessage": null,
  "createdAt": "string",
  "updatedAt": "string"
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Remittance import not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/remittance/imports/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/remittance/imports/{id}/discrepancies`

List reconciliation discrepancies for a remittance import

Returns the persisted reconciliation discrepancies for the import, optionally filtered by sheet and kind. Ordered so every discrepancy of the same product (and eSIM) is contiguous. Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.remittance.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.remittance.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| sheet | query | any | Optional |
| kind | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – Remittance import discrepancies

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "sheet": "subscribers",
      "rowRef": "string",
      "productId": null,
      "columnKey": null,
      "fileValue": null,
      "ourValue": null,
      "kind": "missing_in_db",
      "createdAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid parameters or query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Remittance import not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/remittance/imports/00000000-0000-4000-8000-000000000000/discrepancies?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/remittance/imports/{id}/download`

Download the stored 1Global remittance note (.xlsx)

Streams the stored original .xlsx for the given remittance import. Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.remittance.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.remittance.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Remittance note .xlsx

Content-Type: `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Remittance import not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – File not stored

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/remittance/imports/00000000-0000-4000-8000-000000000000/download" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/esims/admin/remittance/imports/{id}/mapping`

Resolve column mapping for a remittance import awaiting mapping

Persists the resolved column-to-header mapping for an import in pending_mapping, moves it to pending, and enqueues reconciliation. Rejects a mapping that omits a required column (400), an import that is not awaiting mapping (409), or an unknown import (404). Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.remittance.import

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.remittance.import

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "mapping": {
    "subscribers": {
      "key": "string"
    },
    "activation": {
      "key": "string"
    },
    "cover": {
      "key": "string"
    }
  }
}
```

### Responses

**200** – Mapping saved and reconciliation enqueued

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "period": "string",
  "status": "pending_mapping"
}
```

**400** – Invalid or incomplete column mapping

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Remittance import not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Remittance import is not awaiting column mapping

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to save column mapping

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/remittance/imports/00000000-0000-4000-8000-000000000000/mapping" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"mapping\": {
    \"subscribers\": {
      \"key\": \"string\"
    },
    \"activation\": {
      \"key\": \"string\"
    },
    \"cover\": {
      \"key\": \"string\"
    }
  }
}"
```

## POST `/esims/admin/remittance/imports/{id}/reconcile`

Re-run reconciliation for a remittance import

Resets a reconciled, failed or stuck (reconciling) import back to pending and enqueues reconciliation again, overwriting the previous results and discrepancies. Rejects an import that is already queued (pending) or awaiting column mapping (409), or an unknown import (404). Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.remittance.import

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.remittance.import

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Import reset to pending and reconciliation enqueued

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "period": "string",
  "status": "pending_mapping"
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Remittance import not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Remittance import is pending or awaiting column mapping

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to re-run reconciliation

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/remittance/imports/00000000-0000-4000-8000-000000000000/reconcile" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/remittance/imports/{id}/row-comparison`

Compare a single remittance row against our records

Recomputes, for one row of the requested sheet, the full per-column comparison of the file value against our current records (all comparable fields, not just the differing ones). Either side may be absent: a row present only in the file has dbExists=false, a row present only in our records has fileExists=false. Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.remittance.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.remittance.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| sheet | query | any | Required |
| rowRef | query | any | Required |
| productId | query | any | Optional |

### Responses

**200** – Per-column comparison for the row

Content-Type: `application/json`

```json
{
  "sheet": "subscribers",
  "rowRef": "string",
  "fileExists": true,
  "dbExists": true,
  "fields": [
    {
      "columnKey": "string",
      "fileValue": null,
      "ourValue": null,
      "different": true
    }
  ]
}
```

**400** – Invalid parameters or query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Remittance import not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – File not stored

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load remittance import

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/remittance/imports/00000000-0000-4000-8000-000000000000/row-comparison?sheet=subscribers&rowRef=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/admin/remittance/imports/{id}/rows`

List parsed rows of a remittance import sheet

Re-parses the stored .xlsx and returns the parsed rows of the requested sheet (subscribers or activation), paged. Once the import has reconciled, the allow-list columns carry their reconciliation status (matched plus the value-mismatched columns). Superadmin only: the data is platform-global (1Global settles against Covo as a whole), so access is asserted explicitly in the handler on top of the feature gate.

Requires features: esims.remittance.view

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.remittance.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| sheet | query | any | Required |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – Parsed remittance rows

Content-Type: `application/json`

```json
{
  "sheet": "subscribers",
  "items": [
    {
      "rowRef": null,
      "cells": {
        "key": "string"
      },
      "reconciliation": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid parameters or query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Remittance import not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – File not stored

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/admin/remittance/imports/00000000-0000-4000-8000-000000000000/rows?sheet=subscribers&page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/esims/admin/workers/sync-usage`

Run sync-usage-snapshots worker inline

Executes the hourly usage-sync worker synchronously in the request handler. Used for integration tests and operational diagnostics; the scheduler normally invokes this worker on a cron. Request body is ignored. The trigger is recorded in the audit trail as `meta.background-job.run` against the calling admin, including runs that fail.

Requires features: esims.admin.update

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.admin.update

### Responses

**200** – Worker completed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**500** – Worker failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/admin/workers/sync-usage" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/dashboards/active-count`

Active eSIM count

Returns the count of eSIMs currently in status=active scoped to the caller's tenant/organization, plus the count at the start of the requested date-range preset (default last_30_days; approximated by currently-active eSIMs that already existed before the cutoff).

Requires features: analytics.view, esims.admin.list

**Tags:** eSIM Dashboards

**Requires authentication.**

**Features:** analytics.view, esims.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| preset | query | any | Optional |

### Responses

**200** – Active eSIM count with monthly comparison

Content-Type: `application/json`

```json
{
  "value": 1,
  "comparison": null,
  "fetchedAt": "string"
}
```

**500** – Failed to load active eSIM count

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/dashboards/active-count" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/device-support`

Check whether a device supports eSIM

Given an iOS hardware model identifier (from `hw.machine`, e.g. "iPhone15,3"), returns whether the device supports eSIM. Known models resolve to a certain "supported"/"unsupported" verdict from an admin-editable table; unrecognized-but-newer models default to "unknown" (supported=true, optimistic). Unauthenticated; cacheable for 24 hours. Android does not use this endpoint (handled natively in the app).

**Tags:** eSIMs

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| model | query | any | Required |
| platform | query | any | Required |
| osVersion | query | any | Optional |

### Responses

**200** – Device support verdict

Content-Type: `application/json`

```json
{
  "supported": true,
  "status": "supported"
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to determine eSIM support

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/device-support?model=string&platform=ios" \
  -H "Accept: application/json"
```

## GET `/esims/list`

List user eSIMs

Returns the authenticated user's eSIMs. Per eSIM: dataLeftBytes is the wallet balance converted to data at the eSIM's current-country price (uncapped); usedBytes is data consumed since the last top-up summed across all addons; totalAllowanceBytes = dataLeftBytes + usedBytes; usagePercent = usedBytes / totalAllowanceBytes; wallets breaks the figure down per wallet type plus a 'total' row. Deprecated: `totalBytes` and `dataLimitBytes` mirror `totalAllowanceBytes` (kept for older app builds).

Requires features: esims.list

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – eSIM list

Content-Type: `application/json`

```json
{
  "esims": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "iccid": null,
      "status": "pending",
      "phoneNumber": null,
      "countryCode": null,
      "usedBytes": 1,
      "dataLeftBytes": null,
      "totalAllowanceBytes": null,
      "usagePercent": null,
      "wallets": [
        {
          "type": "standard",
          "balanceEuroCents": 1,
          "dataLeftBytes": null
        }
      ],
      "totalBytes": 1,
      "dataLimitBytes": null,
      "disableEsimInHomeZone": true
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/list?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/usage-analytics`

Get usage analytics for a metric and period, bucketed server-side

Returns usage analytics for the authenticated consumer across all their eSIMs. `metric=data|cost` returns chronologically-ordered `buckets` (each carrying both `usedBytes` and `costCents`); `metric=country` returns `items` ordered by `usedBytes` descending with one entry per attributed country (no cap). Period drives bucketing: `7d` → 7 daily buckets (weekday labels), `1m` → 4 weekly buckets (W1–W4), `12m` → 12 monthly buckets (month labels). Snapshots with no country attribution are summed into `unknownBytes` (counted in `totalUsedBytes`). All bucketing is in UTC. Empty windows return `buckets: []` / `items: []` with zero totals. Cost is the actual billed amount; usage not yet billed contributes 0 cost until reconciled. For `metric=country`, unknown/multi-country cost is included in `totalCostCents` only, so it may exceed the sum of per-country `costCents`. For non-EUR users a `displayCurrency`, per-bucket/per-country `displayCostCents` and top-level `displayTotalCostCents` are included: the frozen amount when billed in the user’s currency, otherwise the EUR amount converted at the current rate. Omitted for EUR users or when FX is unavailable.

Requires features: esims.usage

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.usage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| metric | query | any | Required |
| period | query | any | Required |

### Responses

**200** – Usage analytics for the requested metric and period

Content-Type: `application/json`

```json
{
  "metric": "data",
  "period": "7d",
  "periodStart": "string",
  "periodEnd": "string",
  "buckets": [
    {
      "label": "string",
      "bucketStart": "string",
      "bucketEnd": "string",
      "usedBytes": 1,
      "costCents": 1
    }
  ],
  "totalUsedBytes": 1,
  "totalCostCents": 1,
  "unknownBytes": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/usage-analytics?metric=data&period=7d" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/usage-analytics/by-country`

Get usage and cost grouped by country for a given period

Returns bytes used and cost per country for the requested period (`current` or `YYYY-MM`, UTC calendar month). Cost is the actual billed amount; usage not yet billed contributes 0 cost until reconciled. Snapshots with no single-country attribution are summed into `unknownBytes` and excluded from the item list; their cost is still included in the top-level `totalCostCents`, so `totalCostCents` may exceed the sum of per-country `costCents`. For non-EUR users `displayCurrency`, per-country `displayCostCents` and top-level `displayTotalCostCents` are included: the frozen amount when billed in the user’s currency, otherwise the EUR amount converted at the current rate. Omitted for EUR users or when FX is unavailable.

Requires features: esims.usage

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.usage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| period | query | any | Optional |

### Responses

**200** – Per-country usage summary

Content-Type: `application/json`

```json
{
  "periodStart": "string",
  "periodEnd": "string",
  "items": [
    {
      "countryCode": "string",
      "usedBytes": 1,
      "costCents": 1
    }
  ],
  "unknownBytes": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/usage-analytics/by-country?period=current" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/usage-analytics/cost`

Get total usage and cost per month for the last N UTC calendar months

Returns one row per UTC calendar month (most recent first) covering the requested window. Cost is the actual billed amount; usage not yet billed contributes 0 cost until reconciled. For non-EUR users a top-level `displayCurrency` and per-month `displayTotalCostCents` are included: the frozen amount when billed in the user’s currency, otherwise the EUR amount converted at the current rate. Omitted for EUR users or when FX is unavailable.

Requires features: esims.usage

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.usage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| months | query | any | Optional |

### Responses

**200** – Monthly cost timeline

Content-Type: `application/json`

```json
{
  "items": [
    {
      "period": "string",
      "totalUsedBytes": 1,
      "totalCostCents": 1
    }
  ]
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/usage-analytics/cost?months=6" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/usage-analytics/current`

Get current-month usage and cost per eSIM

Returns total bytes used and total cost for the current calendar month (UTC) along with a per-eSIM breakdown. Cost is the actual billed amount; usage not yet billed contributes 0 cost until reconciled. For non-EUR users a `displayCurrency` and `display*` cost fields are included (per-eSIM `displayCostCents`, top-level `displayTotalCostCents`): the frozen amount when it was billed in the user’s currency, otherwise the EUR amount converted at the current rate. Omitted for EUR users or when FX is unavailable.

Requires features: esims.usage

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.usage

### Responses

**200** – Current-period usage summary

Content-Type: `application/json`

```json
{
  "periodStart": "string",
  "periodEnd": "string",
  "totalUsedBytes": 1,
  "totalCostCents": 1,
  "unknownBytes": 1,
  "perEsim": [
    {
      "esimId": "00000000-0000-4000-8000-000000000000",
      "usedBytes": 1,
      "costCents": 1
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/usage-analytics/current" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/usage-analytics/timeline`

Get daily usage timeline across all eSIMs

Returns a daily breakdown of bytes used across all the consumer's eSIMs over the last N days (default 7, max 90).

Requires features: esims.usage

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.usage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| days | query | any | Optional |

### Responses

**200** – Daily usage timeline

Content-Type: `application/json`

```json
{
  "items": [
    {
      "date": "string",
      "usedBytes": 1
    }
  ]
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/usage-analytics/timeline?days=7" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/esims/usage-summary`

Get aggregated usage summary

Aggregated usage across all active eSIMs. dataLeftBytes is the combined wallet balance converted to data at the cheapest active-eSIM country price (uncapped); usedBytes is data used since the last top-up summed across all eSIMs/addons; totalAllowanceBytes = dataLeftBytes + usedBytes; usagePercent = usedBytes / totalAllowanceBytes; wallets breaks the combined balance down per wallet type plus a `total` row. Deprecated: `totalUsedBytes` mirrors usedBytes, `totalAvailableBytes` mirrors dataLeftBytes, `dataLimitBytes` mirrors totalAllowanceBytes (kept for older app builds).

Requires features: esims.usage

**Tags:** eSIMs

**Requires authentication.**

**Features:** esims.usage

### Responses

**200** – Usage summary

Content-Type: `application/json`

```json
{
  "usedBytes": 1,
  "dataLeftBytes": null,
  "totalAllowanceBytes": null,
  "usagePercent": null,
  "wallets": [
    {
      "type": "standard",
      "balanceEuroCents": 1,
      "dataLeftBytes": null
    }
  ],
  "totalUsedBytes": 1,
  "totalAvailableBytes": null,
  "dataLimitBytes": null,
  "activeEsimCount": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/esims/usage-summary" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/esims/webhooks/1global`

Receive 1Global event notifications

Public endpoint secured by Basic Authentication. Handles eSIM lifecycle events from 1Global Connect API.

**Tags:** Webhooks

### Request Body

Content-Type: `application/json`

```json
{
  "id": "string",
  "type": "string",
  "created_at": "string"
}
```

### Responses

**200** – Event received and processed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**413** – Payload too large

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Processing failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/esims/webhooks/1global" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\",
  \"type\": \"string\",
  \"created_at\": \"string\"
}"
```

## GET `/events`

List declared events

Returns every declared event. Filters: category, module, excludeTriggerExcluded (default true).

Requires features: workflows.view

**Tags:** Events

**Requires authentication.**

**Features:** workflows.view

### Responses

**200** – Declared events

Content-Type: `application/json`

```json
{
  "data": [
    {
      "id": "string",
      "label": "string"
    }
  ],
  "total": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/events" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/events/stream`

GET /events/stream

**Tags:** Events

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/events/stream" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/families`

Create a family

Creates a new family with the authenticated user as admin. Returns 409 if the user already belongs to a family.

Requires features: families.create

**Tags:** Families

**Requires authentication.**

**Features:** families.create

### Responses

**201** – Family created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "ownerUserId": "00000000-0000-4000-8000-000000000000",
  "role": "admin"
}
```

**409** – User already belongs to a family

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/families" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/families/{id}`

Delete a family

Soft-deletes the family and all active memberships. Only the family admin (owner) may delete. Returns the list of user ids removed so billing subscribers can revert per-user configuration.

Requires features: families.delete

**Tags:** Families

**Requires authentication.**

**Features:** families.delete

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Family deleted

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "removedUserIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/families/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/families/{id}/invitations`

List family invitations

Returns all invitation codes for the family. Only the family admin (owner) can view the list.

Requires features: families.invitations.view

**Tags:** Families

**Requires authentication.**

**Features:** families.invitations.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Invitation list

Content-Type: `application/json`

```json
{
  "invitations": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "code": "string",
      "invitedName": null,
      "createdByUserId": "00000000-0000-4000-8000-000000000000",
      "usedByConsumerId": null,
      "usedAt": null,
      "expiresAt": null,
      "revokedAt": null,
      "createdAt": "string"
    }
  ]
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/families/00000000-0000-4000-8000-000000000000/invitations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/families/{id}/invitations`

Generate a family invitation code

Generates a new family invitation code. Only the family admin (owner) can generate invitations. Codes expire after 7 days. Limited to 10 active invitations per family and rate limited to 3 requests per 60 seconds per user. Pass role "child" to create a child-role invitation.

Requires features: families.invitations.generate

**Tags:** Families

**Requires authentication.**

**Features:** families.invitations.generate

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "role": "member"
}
```

### Responses

**201** – Invitation generated

Content-Type: `application/json`

```json
{
  "code": "string",
  "expiresAt": "string"
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Active invitation limit reached

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/families/00000000-0000-4000-8000-000000000000/invitations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"role\": \"member\"
}"
```

## DELETE `/families/{id}/invitations/{code}`

Revoke a family invitation code

Revokes a family invitation code. Only the family admin (owner) can revoke invitations. Cannot revoke an already-used or already-revoked invitation.

Requires features: families.invitations.revoke

**Tags:** Families

**Requires authentication.**

**Features:** families.invitations.revoke

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| code | path | any | Required |

### Responses

**200** – Invitation revoked

Content-Type: `application/json`

```json
{
  "code": "string",
  "revokedAt": "string"
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family or invitation not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Invitation already revoked or already used

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/families/00000000-0000-4000-8000-000000000000/invitations/string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/families/{id}/leave`

Leave a family

Removes the authenticated caller from the family (soft-delete) and emits families.family-member.removed. The family admin (owner) cannot leave — they must delete the family instead.

Requires features: families.leave

**Tags:** Families

**Requires authentication.**

**Features:** families.leave

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Left the family

Content-Type: `application/json`

```json
{
  "familyId": "00000000-0000-4000-8000-000000000000",
  "userId": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family or membership not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/families/00000000-0000-4000-8000-000000000000/leave" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/families/{id}/members`

List family members

Returns members of the family, each with their spending cap and monthlySpentEuroCents (eSIM spend for the current calendar month in UTC; a live sum with no stored reset). Only members of the family can view the list. Every member also carries the canonical EUR cap range (capRangeEuroCents). Members with a non-EUR display currency additionally get display-currency variants in their own currency: displaySpendingCap (the frozen EUR cap converted + smart-rounded), displayMonthlySpent (live), and displayCapRange (the localized, hysteresis-dampened slider bounds). All display fields are omitted for EUR members and on FX failure.

Requires features: families.members.view

**Tags:** Families

**Requires authentication.**

**Features:** families.members.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Member list

Content-Type: `application/json`

```json
{
  "members": [
    {
      "userId": "00000000-0000-4000-8000-000000000000",
      "role": "admin",
      "invitedName": null,
      "spendingCapEuroCents": null,
      "monthlySpentEuroCents": 1,
      "joinedAt": "string",
      "capRangeEuroCents": {
        "minEuroCents": 1,
        "maxEuroCents": 1
      }
    }
  ]
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/families/00000000-0000-4000-8000-000000000000/members" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/families/{id}/members/{userId}`

Remove a family member

Admin-only. Removes a member from the family (soft-delete) and emits families.family-member.removed. The admin (owner) cannot be removed — delete the family instead.

Requires features: families.members.remove

**Tags:** Families

**Requires authentication.**

**Features:** families.members.remove

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| userId | path | any | Required |

### Responses

**200** – Member removed

Content-Type: `application/json`

```json
{
  "familyId": "00000000-0000-4000-8000-000000000000",
  "userId": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid params or cannot remove the owner

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family or member not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/families/00000000-0000-4000-8000-000000000000/members/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/families/{id}/members/{userId}`

Update a family member

Admin-only (family owner). Updates a member's display name, role, and/or spending cap in a single call. Only provided fields are changed. The cap is entered in the member's display currency ({ spendingCap: { currency, amountMinorUnits } }); the backend derives the EUR canonical value at set time and freezes the entered amount so it always reads back verbatim (spendingCap: null clears it). Role is restricted to "member" or "child"; the family admin's own role and cap cannot be changed. Emits families.family-member.role-changed and/or families.family-member.cap-changed when those fields change.

Requires features: families.members.update

**Tags:** Families

**Requires authentication.**

**Features:** families.members.update

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| userId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "spendingCap": null
}
```

### Responses

**200** – Member updated

Content-Type: `application/json`

```json
{
  "familyId": "00000000-0000-4000-8000-000000000000",
  "userId": "00000000-0000-4000-8000-000000000000",
  "invitedName": null,
  "role": "admin",
  "spendingCapEuroCents": null
}
```

**400** – Invalid params, invalid/empty body, spending cap outside the allowed range, or cannot set role/cap for the family admin

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family or member not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Cap currency could not be converted to EUR

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/families/00000000-0000-4000-8000-000000000000/members/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"spendingCap\": null
}"
```

## GET `/families/admin`

List all families

Returns a paginated list of all families with owner enrichment. The optional search query matches the family owner or any member by email or name (and by the admin-set member name). When the query is a UUID it also matches the family id and owner user id.

Requires features: families.admin.list

**Tags:** Families Admin

**Requires authentication.**

**Features:** families.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| limit | query | any | Optional |
| offset | query | any | Optional |
| search | query | any | Optional |

### Responses

**200** – Family list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "ownerUserId": "00000000-0000-4000-8000-000000000000",
      "ownerEmail": "string",
      "ownerName": "string",
      "memberCount": 1,
      "invitationCount": 1,
      "createdAt": "string"
    }
  ],
  "total": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/families/admin?limit=20&offset=0" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/families/admin/{id}`

Admin delete family

Soft-deletes a family and all its members, scoped to the caller organization/tenant (a family in another organization reads as not found). No owner check — admin only.

Requires features: families.admin.delete

**Tags:** Families Admin

**Requires authentication.**

**Features:** families.admin.delete

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Family deleted

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "removedUserIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family not found (or outside caller organization)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/families/admin/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/families/admin/{id}`

Get family detail

Returns a single family with owner enrichment and full member list, scoped to the caller organization/tenant (a family in another organization reads as not found). Each member includes monthlySpentEuroCents (eSIM spend for the current calendar month in UTC; a live sum with no stored reset) and the canonical EUR cap range (capRangeEuroCents). Members with a non-EUR display currency additionally get display-currency variants in their own currency: displaySpendingCap, displayMonthlySpent, and the localized displayCapRange; these are omitted for EUR members and on FX failure.

Requires features: families.admin.view

**Tags:** Families Admin

**Requires authentication.**

**Features:** families.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Family detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "ownerUserId": "00000000-0000-4000-8000-000000000000",
  "ownerEmail": "string",
  "ownerName": "string",
  "memberCount": 1,
  "invitationCount": 1,
  "createdAt": "string",
  "updatedAt": "string",
  "deletedAt": null,
  "members": [
    {
      "userId": "00000000-0000-4000-8000-000000000000",
      "email": "string",
      "name": "string",
      "invitedName": null,
      "role": "admin",
      "spendingCapEuroCents": null,
      "monthlySpentEuroCents": 1,
      "joinedAt": "string",
      "capRangeEuroCents": {
        "minEuroCents": 1,
        "maxEuroCents": 1
      }
    }
  ]
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family not found (or outside caller organization)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/families/admin/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/families/admin/{id}/invitations`

List family invitations (admin)

Returns all invitation codes for a family scoped to the caller organization/tenant (a family in another organization reads as not found). No owner check. Shows all invitations including revoked ones.

Requires features: families.admin.view

**Tags:** Families Admin

**Requires authentication.**

**Features:** families.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Invitation list

Content-Type: `application/json`

```json
{
  "invitations": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "code": "string",
      "invitedName": null,
      "createdByUserId": "00000000-0000-4000-8000-000000000000",
      "usedByConsumerId": null,
      "usedAt": null,
      "expiresAt": null,
      "revokedAt": null,
      "createdAt": "string"
    }
  ]
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family not found (or outside caller organization)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/families/admin/00000000-0000-4000-8000-000000000000/invitations" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/families/admin/{id}/invitations/{code}`

Admin revoke a family invitation

Feature-gated admin endpoint. Revokes any invitation from any family without owner check. Cannot revoke an already-used or already-revoked invitation.

Requires features: families.admin.invitations.revoke

**Tags:** Families Admin

**Requires authentication.**

**Features:** families.admin.invitations.revoke

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| code | path | any | Required |

### Responses

**200** – Invitation revoked

Content-Type: `application/json`

```json
{
  "code": "string",
  "revokedAt": "string"
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family or invitation not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Invitation already revoked or already used

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/families/admin/00000000-0000-4000-8000-000000000000/invitations/string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/families/admin/{id}/members/{userId}`

Admin remove a family member

Superadmin-only. Removes any member from any family without owner check. The owner cannot be removed — delete the family instead.

Requires features: families.admin.members.remove

**Tags:** Families Admin

**Requires authentication.**

**Features:** families.admin.members.remove

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| userId | path | any | Required |

### Responses

**200** – Member removed

Content-Type: `application/json`

```json
{
  "familyId": "00000000-0000-4000-8000-000000000000",
  "userId": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid params or cannot remove the owner

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family or member not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/families/admin/00000000-0000-4000-8000-000000000000/members/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/families/admin/{id}/members/{userId}`

Admin update a family member

Superadmin-only. Updates any member's display name, role, and/or spending cap in a single call without owner check. Only provided fields are changed. The cap is entered in the member's display currency ({ spendingCap: { currency, amountMinorUnits } }); the backend derives the EUR canonical value at set time and freezes the entered amount so it always reads back verbatim (spendingCap: null clears it). Role is restricted to "member" or "child"; the family owner's role and cap cannot be changed. Emits families.family-member.role-changed and/or families.family-member.cap-changed when those fields change.

Requires features: families.admin.members.update

**Tags:** Families Admin

**Requires authentication.**

**Features:** families.admin.members.update

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| userId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "spendingCap": null
}
```

### Responses

**200** – Member updated

Content-Type: `application/json`

```json
{
  "familyId": "00000000-0000-4000-8000-000000000000",
  "userId": "00000000-0000-4000-8000-000000000000",
  "invitedName": null,
  "role": "admin",
  "spendingCapEuroCents": null
}
```

**400** – Invalid params, invalid/empty body, spending cap outside the allowed range, or cannot set role/cap for the family owner

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Family or member not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Cap currency could not be converted to EUR

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/families/admin/00000000-0000-4000-8000-000000000000/members/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"spendingCap\": null
}"
```

## POST `/families/join`

Join a family

Joins the authenticated user to a family using a valid invitation code. Returns 409 if the code is already used, revoked, or the user is already in a family.

Requires features: families.join

**Tags:** Families

**Requires authentication.**

**Features:** families.join

### Request Body

Content-Type: `application/json`

```json
{
  "code": "string"
}
```

### Responses

**201** – Joined the family

Content-Type: `application/json`

```json
{
  "familyId": "00000000-0000-4000-8000-000000000000",
  "userId": "00000000-0000-4000-8000-000000000000",
  "role": "admin"
}
```

**400** – Missing or invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Invitation or family not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Invitation already used, revoked, or user already in family

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Family is full or consumer profile not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/families/join" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"code\": \"string\"
}"
```

## GET `/families/lookup`

Look up a family by invitation code

Returns family details for a valid invitation code. Returns 409 if the code is expired, revoked, or already used. Rate limited to 10 requests/min per user.

Requires features: families.view

**Tags:** Families

**Requires authentication.**

**Features:** families.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| code | query | any | Required |

### Responses

**200** – Family preview

Content-Type: `application/json`

```json
{
  "familyId": "00000000-0000-4000-8000-000000000000",
  "ownerUserId": "00000000-0000-4000-8000-000000000000",
  "memberCount": 1,
  "code": "string",
  "expiresAt": null
}
```

**400** – Missing or invalid code param

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Invitation or family not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Invitation expired, revoked, or already used

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/families/lookup?code=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/families/me`

Get the authenticated user's family

Returns the family the user belongs to, or null when the user has no family yet.

Requires features: families.view

**Tags:** Families

**Requires authentication.**

**Features:** families.view

### Responses

**200** – Family or null

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/families/me" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/families/me/owner-wallet`

Get the family owner's wallet balance

Lets an adult family member (role `member`) preview the family owner's standard, bonus, and combined wallet balances — the shared pot their eSIM usage is billed against.

Balances are returned in EUR (canonical) plus the owner's `displayCurrency` when set. Only an adult family member (role `member`) may call this; the owner (`admin`) receives `403` and should use `/api/wallet/me/wallets`.

Requires features: families.owner.wallet.view

**Tags:** Families

**Requires authentication.**

**Features:** families.owner.wallet.view

### Responses

**200** – Family owner's wallets

Content-Type: `application/json`

```json
{
  "wallets": [
    {
      "id": null,
      "type": "standard",
      "balance": {
        "amountInMinorUnits": 1,
        "currency": "string"
      }
    }
  ]
}
```

**404** – Caller is not part of a family

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/families/me/owner-wallet" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/families/me/top-up-owner`

Top up the family owner's wallet

Lets an adult family member (role `member`) fund the family owner's wallet by sending a gift that is redeemed automatically in the same transaction — debiting the caller's standard wallet for the EUR-equivalent of the requested amount (FIFO over credit batches in the caller's `displayCurrency`) and crediting the owner's standard wallet.

`originalCurrency` MUST equal the caller's `displayCurrency` (else `409`). `originalAmountMinorUnits` MUST fall within the gift bounds for that currency — floor = the caller's localized top-up minimum, ceiling = the €200-equivalent gift cap, both as served by `GET /api/gifts/me/gifts/bounds` (else `422`). Only an adult family member (role `member`) may call this; the owner (`admin`) receives `403`.

Requires features: families.owner.top-up

**Tags:** Families

**Requires authentication.**

**Features:** families.owner.top-up

### Request Body

Content-Type: `application/json`

```json
{
  "originalCurrency": "string",
  "originalAmountMinorUnits": 1
}
```

### Responses

**201** – Owner wallet topped up

Content-Type: `application/json`

```json
{
  "giftId": "00000000-0000-4000-8000-000000000000",
  "amountEuroCents": 1,
  "originalCurrency": "string",
  "originalAmountMinorUnits": 1,
  "status": "claimed"
}
```

**400** – Validation error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Caller is not part of a family

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – currency_mismatch (originalCurrency != displayCurrency)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – insufficient_balance, amount_out_of_range, amount_below_minimum (the live EUR value is under the €5 floor even though the amount is inside the published localized bounds), or bounds_unavailable (FX briefly unavailable — retry)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Either gifting is switched off entirely (`giftsEnabled: false` on the app config), or `giftFundingMode` is set to 'payment', whose direct top-up-owner path is not implemented yet (SPEC-078). No top-up is created and no money moves.

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/families/me/top-up-owner" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"originalCurrency\": \"string\",
  \"originalAmountMinorUnits\": 1
}"
```

## GET `/feature_toggles/check/boolean`

Check if feature is enabled

Checks if a feature toggle is enabled for the current context.

**Tags:** Feature Toggles

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| identifier | query | any | Required. Feature toggle identifier |

### Responses

**200** – Feature status

Content-Type: `application/json`

```json
{
  "enabled": true,
  "source": "override",
  "toggleId": "string",
  "identifier": "string",
  "tenantId": "string"
}
```

**400** – Bad Request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Tenant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/feature_toggles/check/boolean?identifier=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/feature_toggles/check/json`

Get json config

Gets the json configuration for a feature toggle.

**Tags:** Feature Toggles

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| identifier | query | any | Required. Feature toggle identifier |

### Responses

**200** – Json config

Content-Type: `application/json`

```json
{
  "valueType": "json",
  "source": "override",
  "toggleId": "string",
  "identifier": "string",
  "tenantId": "string"
}
```

**400** – Bad Request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Tenant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/feature_toggles/check/json?identifier=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/feature_toggles/check/number`

Get number config

Gets the number configuration for a feature toggle.

**Tags:** Feature Toggles

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| identifier | query | any | Required. Feature toggle identifier |

### Responses

**200** – Number config

Content-Type: `application/json`

```json
{
  "valueType": "number",
  "value": 1,
  "source": "override",
  "toggleId": "string",
  "identifier": "string",
  "tenantId": "string"
}
```

**400** – Bad Request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Tenant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/feature_toggles/check/number?identifier=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/feature_toggles/check/string`

Get string config

Gets the string configuration for a feature toggle.

**Tags:** Feature Toggles

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| identifier | query | any | Required. Feature toggle identifier |

### Responses

**200** – String config

Content-Type: `application/json`

```json
{
  "valueType": "string",
  "value": "string",
  "source": "override",
  "toggleId": "string",
  "identifier": "string",
  "tenantId": "string"
}
```

**400** – Bad Request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Tenant not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/feature_toggles/check/string?identifier=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/feature_toggles/global`

Delete global feature toggle

Soft deletes a global feature toggle by ID. Requires superadmin role.

Requires features: feature_toggles.global.manage

**Tags:** Feature Toggles

**Requires authentication.**

**Features:** feature_toggles.global.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required. Feature toggle identifier |

### Responses

**200** – Feature toggle deleted

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Feature toggle not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/feature_toggles/global?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/feature_toggles/global`

List global feature toggles

Returns all global feature toggles with filtering and pagination. Requires superadmin role.

Requires features: feature_toggles.view

**Tags:** Feature Toggles

**Requires authentication.**

**Features:** feature_toggles.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional. Page number for pagination |
| pageSize | query | any | Optional. Number of items per page (max 200) |
| search | query | any | Optional. Case-insensitive search across identifier, name, description, and category |
| type | query | any | Optional. Filter by toggle type (boolean, string, number, json) |
| category | query | any | Optional. Filter by category (case-insensitive partial match) |
| name | query | any | Optional. Filter by name (case-insensitive partial match) |
| identifier | query | any | Optional. Filter by identifier (case-insensitive partial match) |
| sortField | query | any | Optional. Field to sort by |
| sortDir | query | any | Optional. Sort direction (ascending or descending) |

### Responses

**200** – Feature toggles collection

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "identifier": "string",
      "name": "string",
      "description": null,
      "category": null,
      "type": "boolean",
      "defaultValue": null,
      "createdAt": null,
      "updatedAt": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/feature_toggles/global?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/feature_toggles/global`

Create global feature toggle

Creates a new global feature toggle. Requires superadmin role.

Requires features: feature_toggles.global.manage

**Tags:** Feature Toggles

**Requires authentication.**

**Features:** feature_toggles.global.manage

### Request Body

Content-Type: `application/json`

```json
{
  "identifier": "string",
  "name": "string",
  "description": null,
  "category": null,
  "type": "boolean",
  "defaultValue": null
}
```

### Responses

**201** – Feature toggle created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/feature_toggles/global" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"identifier\": \"string\",
  \"name\": \"string\",
  \"description\": null,
  \"category\": null,
  \"type\": \"boolean\",
  \"defaultValue\": null
}"
```

## PUT `/feature_toggles/global`

Update global feature toggle

Updates an existing global feature toggle. Requires superadmin role.

Requires features: feature_toggles.global.manage

**Tags:** Feature Toggles

**Requires authentication.**

**Features:** feature_toggles.global.manage

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "description": null,
  "category": null,
  "defaultValue": null
}
```

### Responses

**200** – Feature toggle updated

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Feature toggle not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/feature_toggles/global" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"description\": null,
  \"category\": null,
  \"defaultValue\": null
}"
```

## GET `/feature_toggles/global/{id}`

Fetch feature toggle by ID

Returns complete details of a feature toggle.

Requires features: feature_toggles.view

**Tags:** Feature Toggles

**Requires authentication.**

**Features:** feature_toggles.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Feature toggle detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "identifier": "string",
  "name": "string",
  "description": null,
  "category": null,
  "type": "boolean",
  "defaultValue": null,
  "createdAt": null,
  "updatedAt": null
}
```

**400** – Invalid identifier

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Feature toggle not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/feature_toggles/global/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/feature_toggles/global/{id}/override`

Fetch feature toggle override

Returns feature toggle override.

Requires features: feature_toggles.view

**Tags:** Feature Toggles

**Requires authentication.**

**Features:** feature_toggles.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Feature toggle overrides

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "tenantName": "string",
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "toggleType": "boolean",
  "updatedAt": null
}
```

**400** – Invalid request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Feature toggle not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/feature_toggles/global/:id/override" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/feature_toggles/overrides`

List overrides

Returns list of feature toggle overrides.

Requires features: feature_toggles.view

**Tags:** Feature Toggles

**Requires authentication.**

**Features:** feature_toggles.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| category | query | any | Optional |
| name | query | any | Optional |
| identifier | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |

### Responses

**200** – List of overrides

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "toggleId": "00000000-0000-4000-8000-000000000000",
      "tenantName": "string",
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "identifier": "string",
      "name": "string",
      "category": "string",
      "isOverride": true
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1,
  "isSuperAdmin": true
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/feature_toggles/overrides?page=1&pageSize=25" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/feature_toggles/overrides`

Change override state

Enable, disable or inherit a feature toggle for a specific tenant.

Requires features: feature_toggles.manage

**Tags:** Feature Toggles

**Requires authentication.**

**Features:** feature_toggles.manage

### Request Body

Content-Type: `application/json`

```json
{
  "toggleId": "00000000-0000-4000-8000-000000000000",
  "isOverride": true
}
```

### Responses

**200** – Override updated

Content-Type: `application/json`

```json
{
  "ok": true,
  "overrideToggleId": null
}
```

**400** – Validation failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/feature_toggles/overrides" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"toggleId\": \"00000000-0000-4000-8000-000000000000\",
  \"isOverride\": true
}"
```

## GET `/feed/admin/config`

List per-card-type feed configuration (merged with defaults)

Requires features: feed.admin.config.manage

**Tags:** Admin · Feed

**Requires authentication.**

**Features:** feed.admin.config.manage

### Responses

**200** – Config rows

Content-Type: `application/json`

```json
{
  "items": [
    {
      "cardType": "top_up_recommended",
      "enabled": true,
      "priority": 1,
      "ttlHours": null,
      "thresholds": {},
      "updatedAt": null
    }
  ]
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/feed/admin/config" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/feed/admin/config`

Update configuration for a card type

Requires features: feed.admin.config.manage

**Tags:** Admin · Feed

**Requires authentication.**

**Features:** feed.admin.config.manage

### Request Body

Content-Type: `application/json`

```json
{
  "cardType": "top_up_recommended",
  "ttlHours": null
}
```

### Responses

**200** – Updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/feed/admin/config" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"cardType\": \"top_up_recommended\",
  \"ttlHours\": null
}"
```

## GET `/feed/me`

Get the priority-sorted homescreen feed for the caller

Runtime-computed cards (top-up recommendation, balance forecast, peak usage) sorted by priority then recency. Dismissed cards are filtered out until their content signature changes. When no signal-driven card applies, a positive "all good" default card is returned instead, so a caller with a consumer profile always receives at least one card.

Requires features: feed.view

**Tags:** Feed

**Requires authentication.**

**Features:** feed.view

### Responses

**200** – Feed cards

Content-Type: `application/json`

```json
{
  "cards": [
    {
      "type": "top_up_recommended",
      "key": "string",
      "signature": "string",
      "priority": 1,
      "title": "string",
      "body": "string",
      "displayAmount": null,
      "meta": {},
      "occurredAt": "string",
      "expiresAt": null
    }
  ]
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/feed/me" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/feed/me/{key}`

Dismiss or mark a feed card as read

Records a per-user dismissal or read state for the feed card with the given key. The signature pins the dismissal to the card content the user saw; a dismissed runtime card reappears once its signature changes.

Requires features: feed.view

**Tags:** Feed

**Requires authentication.**

**Features:** feed.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| key | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "action": "dismiss",
  "signature": "string"
}
```

### Responses

**200** – State recorded

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/feed/me/string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"action\": \"dismiss\",
  \"signature\": \"string\"
}"
```

## GET `/gifts/admin`

List gifts (admin)

Returns a paginated list of gifts scoped to the caller tenant/organization. Supports filtering by status (csv), sender, claimer, and free-text search (q) matching the gift invite code, the sender/claimer by email or name, and the gift amount/currency (e.g. "PLN10", "10 zł", or "EUR"). When the query is a UUID it also matches the gift id, sender user id and claimer user id.

Requires features: gifts.admin.view

**Tags:** Admin · Gifts

**Requires authentication.**

**Features:** gifts.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Required |
| senderUserId | query | any | Optional |
| claimerUserId | query | any | Optional |
| tenantId | query | any | Optional |
| organizationId | query | any | Optional |
| q | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sort | query | any | Optional |

### Responses

**200** – Gift list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "senderUserId": "00000000-0000-4000-8000-000000000000",
      "senderWalletId": "00000000-0000-4000-8000-000000000000",
      "amountEuroCents": 1,
      "originalCurrency": "string",
      "originalAmountMinorUnits": 1,
      "status": "awaiting_payment",
      "inviteCode": "string",
      "inviteCodeId": null,
      "debitTransactionId": null,
      "refundTransactionId": null,
      "claimTransactionId": null,
      "claimedByUserId": null,
      "claimedByConsumerId": null,
      "claimedAt": null,
      "cancelledAt": null,
      "expiredAt": null,
      "expiresAt": "string",
      "tenantId": null,
      "organizationId": null,
      "createdAt": "string"
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/gifts/admin?page=1&pageSize=50&sort=createdAt%3Adesc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/gifts/admin/{id}`

Get gift detail (admin)

Returns a single gift scoped to the caller tenant/organization with linked wallet transactions, invite code summary, and minimal sender/claimer profiles.

Requires features: gifts.admin.view

**Tags:** Admin · Gifts

**Requires authentication.**

**Features:** gifts.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Gift detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "senderUserId": "00000000-0000-4000-8000-000000000000",
  "senderWalletId": "00000000-0000-4000-8000-000000000000",
  "amountEuroCents": 1,
  "originalCurrency": "string",
  "originalAmountMinorUnits": 1,
  "status": "awaiting_payment",
  "inviteCode": {
    "id": null,
    "code": "string",
    "scope": "string",
    "createdAt": "string",
    "expiresAt": null,
    "revokedAt": null,
    "usedAt": null
  },
  "inviteCodeId": null,
  "debitTransactionId": null,
  "refundTransactionId": null,
  "claimTransactionId": null,
  "claimedByUserId": null,
  "claimedByConsumerId": null,
  "claimedAt": null,
  "cancelledAt": null,
  "expiredAt": null,
  "expiresAt": "string",
  "tenantId": null,
  "organizationId": null,
  "createdAt": "string",
  "updatedAt": "string",
  "transactions": {
    "created": null,
    "claimed": null,
    "cancelled": null
  },
  "sender": null,
  "claimer": null
}
```

**400** – Invalid params

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Gift not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/gifts/admin/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/gifts/admin/{id}/cancel`

Admin cancel a pending gift

Admin-initiated cancel of a pending gift. Same `pending`-only constraint as sender-cancel — it only skips the sender-ownership check and records admin attribution. Refunds the gift's `amountEuroCents` to the sender's standard wallet as a `gift_refund` credit batch, revokes the invite code, and records the admin user + optional reason in the action log. Shares the `gift-refund:<giftId>` idempotency key with sender-cancel and the expire-sweep worker so the refund is applied at most once. Sender-initiated cancel lives at `DELETE /api/gifts/me/gifts/[id]`.

Requires features: gifts.admin.cancel

**Tags:** Admin · Gifts

**Requires authentication.**

**Features:** gifts.admin.cancel

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Gift cancelled and refund issued

Content-Type: `application/json`

```json
{
  "giftId": "00000000-0000-4000-8000-000000000000",
  "status": "cancelled",
  "refundTransactionId": "00000000-0000-4000-8000-000000000000",
  "amountEuroCents": 1,
  "originalCurrency": "string",
  "originalAmountMinorUnits": 1
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Gift not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Gift not in pending state

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/gifts/admin/00000000-0000-4000-8000-000000000000/cancel" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/gifts/admin/workers/expire-gifts`

Run expire-gifts sweep worker inline

Executes the gift expiry sweep worker synchronously in the request handler. Used for integration tests and operational diagnostics; the scheduler normally invokes this worker on a 15-minute cron. Request body is ignored. The trigger is recorded in the audit trail as `meta.background-job.run` against the calling admin, including runs that fail.

Requires features: gifts.admin.run_expire_sweep

**Tags:** Gifts

**Requires authentication.**

**Features:** gifts.admin.run_expire_sweep

### Responses

**200** – Worker completed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**500** – Worker failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/gifts/admin/workers/expire-gifts" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/gifts/me/gifts`

List the caller's sent gifts

Returns the caller's gifts, newest first. Scoped to `sender_user_id = auth.sub` and the caller's organization.

`status` may be any of `awaiting_payment`, `pending`, `claimed`, `cancelled`, `expired`. A gift in `awaiting_payment` has been created but its payment has not succeeded yet, so its `code` is the empty string — poll here (or react to the `gifts.gift.created` push) to pick the code up once the payment lands. `fundingSource` distinguishes legacy wallet-funded gifts from payment-funded ones during the cut-over.

Requires features: gifts.view

**Tags:** Gifts

**Requires authentication.**

**Features:** gifts.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – Caller's gifts

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "originalCurrency": "string",
      "originalAmountMinorUnits": 1,
      "amountEuroCents": 1,
      "status": "string",
      "code": "string",
      "fundingSource": "wallet",
      "claimedByUserId": null,
      "claimedAt": null,
      "cancelledAt": null,
      "expiredAt": null,
      "expiresAt": "string",
      "createdAt": "string"
    }
  ],
  "total": 1
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/gifts/me/gifts?limit=20&offset=0" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/gifts/me/gifts`

Send a gift

Creates a gift. **The response shape depends on how gifts are currently funded**, discriminated by `status`; read `giftFundingMode` from `GET /api/meta/config` to know which to expect.

**`giftFundingMode: "payment"` (SPEC-078, the target model) → `status: "awaiting_payment"`.** No wallet is touched. The gift is created unpaid and a charge is raised on the ordinary top-up rails, so the response is a payment session (`clientSecret`, `paymentIntentId`, `customerId`, `customerSessionClientSecret`) to be driven with the same Payment Sheet as `POST /api/me/payments/top-up`. **There is no `code` and no `expiresAt` yet** — the invitation code is minted only when the payment succeeds, at which point the sender receives the `gifts.gift.created` push and the code appears on `GET /api/gifts/me/gifts`. The 7-day window starts at payment success, not at this call. `withdrawalConsent: true` is required. If the payment fails or is abandoned the gift is dropped and no code is ever issued.

**`giftFundingMode: "wallet"` (legacy, being retired) → `status: "pending"`.** Debits the caller's standard wallet for the EUR-equivalent of the requested amount (FIFO over credit batches in the caller's `displayCurrency`) and issues the 7-day invitation code immediately. `amountEuroCents` is the actual EUR debited, which may differ slightly from a today's-rate conversion because the walk honours each top-up's realized rate.

Common to both: `originalCurrency` MUST equal the caller's `displayCurrency` (else `409 currency_mismatch`). `originalAmountMinorUnits` MUST fall within the gift bounds for that currency — floor = the caller's localized top-up minimum, ceiling = the €200-equivalent gift cap, both as served by `GET /api/gifts/me/gifts/bounds` (else `422 amount_out_of_range`). At most 10 gifts may be outstanding at once, counting both `awaiting_payment` and `pending` (else `409 gift_limit_reached`). `displayAmount` echoes the frozen original-currency amount for localized display, omitted when the gift was sent in EUR.

On the payment path only: a caller whose display currency has since been deactivated is charged in EUR rather than refused, and the response then carries `currencyFallback` while `originalCurrency` reflects what was actually charged. Pass `idempotencyKey` so a retried or double-tapped create reuses the same charge instead of raising a second one.

Gifting can be switched off entirely: when `giftsEnabled` is false on `GET /api/meta/config`, this endpoint returns `503` under either funding mode and nothing is charged or debited. Clients SHOULD hide the gifting entry point on that flag. Gifts that already exist are unaffected — they stay listable, claimable and refundable.

Requires features: gifts.send

**Tags:** Gifts

**Requires authentication.**

**Features:** gifts.send

### Request Body

Content-Type: `application/json`

```json
{
  "originalCurrency": "string",
  "originalAmountMinorUnits": 1
}
```

### Responses

**201** – Gift created. `status: "awaiting_payment"` returns a payment session with no code; `status: "pending"` returns a ready-to-share wallet-funded gift.

Content-Type: `application/json`

```json
{
  "status": "pending",
  "giftId": "00000000-0000-4000-8000-000000000000",
  "code": "string",
  "expiresAt": "string",
  "originalCurrency": "string",
  "originalAmountMinorUnits": 1,
  "amountEuroCents": 1
}
```

**400** – Validation error, or the caller has no email address on file

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**402** – Payment mode only: the payment platform declined the gift charge. `code` carries the decline code and `paymentIntentId` the intent, when the platform reported them. No gift remains.

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – gift_limit_reached (≥10 outstanding, counting awaiting_payment and pending) or currency_mismatch (originalCurrency != displayCurrency)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – amount_out_of_range, amount_below_minimum (the live EUR value is under the €5 floor even though the amount is inside the published localized bounds), or bounds_unavailable (FX briefly unavailable — retry). Wallet mode adds insufficient_balance. Payment mode adds withdrawal_consent_required, and billing_details_required when the caller has no billing country or no tax rate matches (`reason` is `billing-country-missing` or `no-matching-rate`).

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Gifting is switched off (`giftsEnabled: false` on the app config). No gift is created, no wallet is debited and no charge is raised.

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/gifts/me/gifts" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"originalCurrency\": \"string\",
  \"originalAmountMinorUnits\": 1
}"
```

## DELETE `/gifts/me/gifts/{id}`

Cancel a pending gift

Refunds the gift's `amountEuroCents` to the sender's standard wallet as a `gift_refund` credit batch that inherits the gift's original currency and amount, then revokes the invite code. Only `pending` gifts and only the sender can cancel.

Requires features: gifts.cancel

**Tags:** Gifts

**Requires authentication.**

**Features:** gifts.cancel

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Gift cancelled and refund issued

Content-Type: `application/json`

```json
{
  "giftId": "00000000-0000-4000-8000-000000000000",
  "status": "string",
  "refundTransactionId": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Gift not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Gift not in pending state. Includes `awaiting_payment`: a gift whose payment has not succeeded yet holds no money and has nothing to refund, so it is abandoned by dropping the payment sheet rather than cancelled here. It is swept automatically once its payment window lapses.

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/gifts/me/gifts/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/gifts/me/gifts/bounds`

Gift amount bounds in the caller's display currency

Returns the canonical EUR gift bounds (`boundsEuroCents`: €5–€200, €1 granularity) and, for a non-EUR caller, a `displayBounds` block in their `displayCurrency`. `displayBounds` is omitted for EUR callers and when FX is briefly unavailable, so the client always has usable EUR values and is never told the wrong currency — the same contract as `GET /api/me/payments/top-up/config` and the personal spending-cap status.

`displayBounds.minMinorUnits` **is** the caller's localized top-up minimum: it is read from the very same published value the top-up screen shows, so the two screens cannot disagree. `displayBounds.maxMinorUnits` is the €200-equivalent gift ceiling, rounded **up** to `granularityMinorUnits`, so it may sit slightly above €200 — accepted, since the cap is a product guardrail rather than a payment constraint.

Both localized bounds are hysteresis-dampened: published per tenant/organization/currency and only re-priced when the exchange rate has drifted materially, so they do not jitter from one day to the next.

`displayBounds.minMinorUnits` is NOT guaranteed to be a multiple of `granularityMinorUnits` (the top-up minimum uses its own magnitude rounding — e.g. 21.50 zł against a 10 zł gift granularity). A slider SHOULD start at `minMinorUnits`, then step by `granularityMinorUnits` up to `maxMinorUnits`.

`POST /api/me/gifts` validates against these same localized bounds AND against the canonical €5 floor at the live rate (mirroring `POST /api/me/payments/top-up`), so an amount at the very bottom of the slider can still be rejected with `422` if the rate has drifted since the bound was published.

Requires features: gifts.view

**Tags:** Gifts

**Requires authentication.**

**Features:** gifts.view

### Responses

**200** – Gift bounds in the caller's display currency

Content-Type: `application/json`

```json
{
  "boundsEuroCents": {
    "minEuroCents": 1,
    "maxEuroCents": 1,
    "granularityEuroCents": 1
  }
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/gifts/me/gifts/bounds" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/gifts/me/gifts/claim`

Claim a gift

Claims a pending gift the authenticated user received. Credits the gift amount to the caller's standard wallet (which is created on first access), marks the invitation code used, and activates the caller's consumer profile if it is not already active. Allowed for new and already-active accounts; the sender may not claim their own gift. Rate limited per user and per source IP.

Requires features: gifts.claim

**Tags:** Gifts

**Requires authentication.**

**Features:** gifts.claim

### Request Body

Content-Type: `application/json`

```json
{
  "code": "string"
}
```

### Responses

**200** – Gift claimed and recipient wallet credited

Content-Type: `application/json`

```json
{
  "giftId": "00000000-0000-4000-8000-000000000000",
  "originalCurrency": "string",
  "originalAmountMinorUnits": 1,
  "amountEuroCents": 1,
  "status": "string"
}
```

**400** – Missing or invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Invitation code not found or not a gift

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Invitation already used, gift not pending, or sender attempting self-claim

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**410** – Invitation or gift has expired or been revoked

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Consumer profile not found for the authenticated user

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/gifts/me/gifts/claim" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"code\": \"string\"
}"
```

## GET `/health/live`

Liveness probe

**Tags:** Health

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/health/live" \
  -H "Accept: application/json"
```

## GET `/health/ready`

Readiness probe

**Tags:** Health

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/health/ready" \
  -H "Accept: application/json"
```

## GET `/invite_codes/admin`

List all invite codes (admin)

Returns all invite codes with pagination, status filter, and search. Search matches the code, the creator by email or name, and the redeemer by email or name. When the query is a UUID it also matches the invite code id, creator user id and redeemer consumer id. Includes creator and redeemer info.

Requires features: invite-codes.admin.list

**Tags:** Invite Codes

**Requires authentication.**

**Features:** invite-codes.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| status | query | any | Optional |
| createdByUserId | query | any | Optional |
| search | query | any | Optional |

### Responses

**200** – Paginated list of invite codes

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "code": "string",
      "status": "available",
      "createdByUserId": "00000000-0000-4000-8000-000000000000",
      "createdByName": null,
      "createdByEmail": null,
      "usedByConsumerId": null,
      "usedByName": null,
      "usedByEmail": null,
      "usedAt": null,
      "createdAt": "string",
      "expiresAt": null,
      "revokedAt": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/invite_codes/admin?page=1&pageSize=25&sortField=createdAt&sortDir=desc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/invite_codes/admin/generate`

Generate invite code (admin)

Generates a new invite code without the 3-code consumer limit.

Requires features: invite-codes.admin.create

**Tags:** Invite Codes

**Requires authentication.**

**Features:** invite-codes.admin.create

### Request Body

Content-Type: `application/json`

```json
{
  "expiresAt": null,
  "referenceId": null
}
```

### Responses

**200** – Generated invite code

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "code": "string",
  "createdAt": "string",
  "expiresAt": null
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to generate invite code

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/invite_codes/admin/generate" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"expiresAt\": null,
  \"referenceId\": null
}"
```

## POST `/invite_codes/admin/revoke`

Revoke an invite code (admin)

Revokes an unused invite code. Used codes cannot be revoked.

Requires features: invite-codes.admin.revoke

**Tags:** Invite Codes

**Requires authentication.**

**Features:** invite-codes.admin.revoke

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Code revoked

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**404** – Invite code not found or out of scope

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Code already used

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/invite_codes/admin/revoke" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## POST `/invite_codes/apply`

Apply an invite code to activate account

Applies an invite code to activate the consumer account. Rate limited to 3 requests/min per user.

Requires features: invite-codes.apply

**Tags:** Invite Codes

**Requires authentication.**

**Features:** invite-codes.apply

### Request Body

Content-Type: `application/json`

```json
{
  "code": "string"
}
```

### Responses

**200** – Account activated

Content-Type: `application/json`

```json
{
  "activated": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Invalid code, already active, or code issue

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/invite_codes/apply" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"code\": \"string\"
}"
```

## POST `/invite_codes/generate`

Generate a new invite code

Generates a new invite code for the authenticated active consumer. Limited to 3 active codes per consumer.

Requires features: invite-codes.generate

**Tags:** Invite Codes

**Requires authentication.**

**Features:** invite-codes.generate

### Responses

**200** – Generated invite code

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "code": "string",
  "createdAt": "string",
  "expiresAt": null
}
```

**500** – Failed to generate invite code

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/invite_codes/generate" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/invite_codes/me`

List own invite codes

Returns all invite codes created by the authenticated consumer.

Requires features: invite-codes.list

**Tags:** Invite Codes

**Requires authentication.**

**Features:** invite-codes.list

### Responses

**200** – List of invite codes

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "code": "string",
      "status": "available",
      "usedAt": null,
      "createdAt": "string",
      "expiresAt": null
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/invite_codes/me" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/invite_codes/verify`

Verify an invite code

Checks whether an invite code is valid without consuming it. Rate limited to 3 requests/min per user.

Requires features: invite-codes.verify

**Tags:** Invite Codes

**Requires authentication.**

**Features:** invite-codes.verify

### Request Body

Content-Type: `application/json`

```json
{
  "code": "string"
}
```

### Responses

**200** – Code verification result

Content-Type: `application/json`

```json
{
  "valid": true,
  "scope": "activation",
  "referenceId": null
}
```

**422** – Invalid, expired, or used code

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/invite_codes/verify" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"code\": \"string\"
}"
```

## GET `/invoicing/admin/invoices`

List invoices (admin)

Returns the tenant's invoices - both the in-house invoicing documents (top-up receipts, usage invoices, credit notes) and the legacy Stripe-mirror invoices - merged into one list, newest first. Each item carries a `source` (`invoicing` or `stripe`). Supports optional `documentType` (the in-house types plus `stripe_invoice`), `status`, and `userId` filters plus a `search` term matched against invoice/platform invoice number (and, when the term is a UUID, the id or user id) and `limit`/`offset` pagination.

Requires features: invoicing.admin.list

**Tags:** Invoicing Admin

**Requires authentication.**

**Features:** invoicing.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| documentType | query | any | Optional |
| status | query | any | Optional |
| userId | query | any | Optional |
| search | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – Admin invoice list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "source": "invoicing",
      "userId": "00000000-0000-4000-8000-000000000000",
      "documentType": "string",
      "invoiceNumber": null,
      "status": "string",
      "currency": "string",
      "grandTotalGrossCents": 1,
      "issueDate": "string"
    }
  ],
  "total": 1
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load invoices

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/invoicing/admin/invoices" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/invoicing/admin/invoices/{id}`

Get an invoice with line detail (admin)

Returns a single invoice scoped to the admin caller's tenant/organization, including its line items and billing snapshot.

Requires features: invoicing.admin.view

**Tags:** Invoicing Admin

**Requires authentication.**

**Features:** invoicing.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Invoice detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "userId": "00000000-0000-4000-8000-000000000000",
  "documentType": "topup_invoice",
  "invoiceNumber": "string",
  "status": "issued",
  "currency": "string",
  "periodStart": null,
  "periodEnd": null,
  "issueDate": "string",
  "subtotalNetCents": 1,
  "taxTotalCents": 1,
  "grandTotalGrossCents": 1,
  "vatPercentage": null,
  "reverseCharge": true,
  "remainingCreditCents": null,
  "relatedPaymentIds": null,
  "billingSnapshot": null,
  "lines": [
    {
      "lineNumber": 1,
      "kind": "topup",
      "description": "string",
      "sectionLabel": null,
      "esimId": null,
      "countryCode": null,
      "usageDate": null,
      "dataBytes": null,
      "balanceBytes": null,
      "netCents": 1,
      "taxPercentage": null,
      "taxCents": 1,
      "grossCents": 1
    }
  ]
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Invoice not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load invoice

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/invoicing/admin/invoices/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/invoicing/admin/invoices/{id}/download`

Download an invoice as a PDF (admin)

Renders the invoice/receipt PDF on demand from the stored record. Scoped to the admin caller's tenant/organization.

Requires features: invoicing.admin.view

**Tags:** Invoicing Admin

**Requires authentication.**

**Features:** invoicing.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – PDF document

Content-Type: `application/json`

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Invoice not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load invoice

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/invoicing/admin/invoices/00000000-0000-4000-8000-000000000000/download" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/invoicing/admin/workers/generate-monthly-usage-invoices`

Run generate-monthly-usage-invoices worker inline

Executes the monthly usage invoice generator synchronously in the request handler. Invoices the previous full UTC calendar month for every user with billed usage snapshots in that window; already-issued invoices are skipped. Used for integration tests and operational diagnostics; the scheduler normally invokes this worker on a monthly cron. Request body is ignored. The trigger is recorded in the audit trail as `meta.background-job.run` against the calling admin, including runs that fail.

Requires features: invoicing.manage

**Tags:** Invoicing

**Requires authentication.**

**Features:** invoicing.manage

### Responses

**200** – Worker completed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**500** – Worker failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/invoicing/admin/workers/generate-monthly-usage-invoices" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/invoicing/me/invoices`

List the caller's invoices

Returns the authenticated user's invoices (top-up and usage), newest first. Scoped to `user_id = auth.sub` and the caller's tenant/organization. Supports optional `documentType` filter and `limit`/`offset` pagination.

Requires features: invoicing.view

**Tags:** Invoicing

**Requires authentication.**

**Features:** invoicing.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| documentType | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – Caller's invoices

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "userId": "00000000-0000-4000-8000-000000000000",
      "documentType": "topup_invoice",
      "invoiceNumber": "string",
      "status": "issued",
      "currency": "string",
      "periodStart": null,
      "periodEnd": null,
      "issueDate": "string",
      "subtotalNetCents": 1,
      "taxTotalCents": 1,
      "grandTotalGrossCents": 1
    }
  ],
  "total": 1
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load invoices

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/invoicing/me/invoices" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/invoicing/me/invoices/{id}`

Get one of the caller's invoices with line detail

Returns a single invoice owned by the authenticated user, including its line items and billing snapshot. Scoped to `user_id = auth.sub` and the caller's tenant/organization.

Requires features: invoicing.view

**Tags:** Invoicing

**Requires authentication.**

**Features:** invoicing.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Invoice detail

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "userId": "00000000-0000-4000-8000-000000000000",
  "documentType": "topup_invoice",
  "invoiceNumber": "string",
  "status": "issued",
  "currency": "string",
  "periodStart": null,
  "periodEnd": null,
  "issueDate": "string",
  "subtotalNetCents": 1,
  "taxTotalCents": 1,
  "grandTotalGrossCents": 1,
  "vatPercentage": null,
  "reverseCharge": true,
  "remainingCreditCents": null,
  "relatedPaymentIds": null,
  "billingSnapshot": null,
  "lines": [
    {
      "lineNumber": 1,
      "kind": "topup",
      "description": "string",
      "sectionLabel": null,
      "esimId": null,
      "countryCode": null,
      "usageDate": null,
      "dataBytes": null,
      "balanceBytes": null,
      "netCents": 1,
      "taxPercentage": null,
      "taxCents": 1,
      "grossCents": 1
    }
  ]
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Invoice not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load invoice

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/invoicing/me/invoices/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/invoicing/me/invoices/{id}/download`

Download one of the caller's invoices as a PDF

Renders the invoice/receipt PDF on demand from the stored record. Scoped to `user_id = auth.sub` and the caller's tenant/organization.

Requires features: invoicing.view

**Tags:** Invoicing

**Requires authentication.**

**Features:** invoicing.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – PDF document

Content-Type: `application/json`

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Invoice not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load invoice

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/invoicing/me/invoices/00000000-0000-4000-8000-000000000000/download" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/meta/admin/config`

Read app config (admin)

Returns the app config plus the adapter-declared payment config field descriptors used to render the admin form.

Requires roles: superadmin

**Tags:** Meta

**Requires authentication.**

**Roles:** superadmin

### Responses

**200** – App config record

Content-Type: `application/json`

```json
{
  "paymentPlatform": "stripe",
  "paymentAdapterConfigs": {
    "key": {
      "key": "string"
    }
  },
  "hyperswitchMode": "sandbox",
  "configFields": {
    "key": [
      {
        "name": "string",
        "required": true,
        "label": "string"
      }
    ]
  },
  "giftFundingMode": "wallet",
  "giftsEnabled": true,
  "minAppVersionIos": "string",
  "minAppVersionAndroid": "string",
  "latestAppVersionIos": "string",
  "latestAppVersionAndroid": "string",
  "iosAppLink": "string",
  "androidAppLink": "string",
  "updatedAt": "string"
}
```

**403** – Forbidden

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not initialized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/meta/admin/config" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PATCH `/meta/admin/config`

Update app config (admin)

Payment config values are validated against the active adapters’ declared field schema; invalid values return 400 with fieldErrors keyed <platform>.<field>. `giftFundingMode` switches how new gifts are funded globally — gifts already created keep their own stored source. `giftsEnabled: false` is the master switch: it blocks the creation of new gifts by either funding path, while gifts that already exist stay claimable, cancellable and refundable. This is a full-record replace — every field must be sent.

Requires roles: superadmin

**Tags:** Meta

**Requires authentication.**

**Roles:** superadmin

### Request Body

Content-Type: `application/json`

```json
{
  "paymentPlatform": "stripe",
  "paymentAdapterConfigs": {
    "key": {
      "key": "string"
    }
  },
  "hyperswitchMode": "sandbox",
  "giftFundingMode": "wallet",
  "giftsEnabled": true,
  "minAppVersionIos": "string",
  "minAppVersionAndroid": "string",
  "latestAppVersionIos": "string",
  "latestAppVersionAndroid": "string",
  "iosAppLink": "https://example.com/resource",
  "androidAppLink": "https://example.com/resource"
}
```

### Responses

**200** – Updated app config

Content-Type: `application/json`

```json
{
  "paymentPlatform": "stripe",
  "paymentAdapterConfigs": {
    "key": {
      "key": "string"
    }
  },
  "hyperswitchMode": "sandbox",
  "configFields": {
    "key": [
      {
        "name": "string",
        "required": true,
        "label": "string"
      }
    ]
  },
  "giftFundingMode": "wallet",
  "giftsEnabled": true,
  "minAppVersionIos": "string",
  "minAppVersionAndroid": "string",
  "latestAppVersionIos": "string",
  "latestAppVersionAndroid": "string",
  "iosAppLink": "string",
  "androidAppLink": "string",
  "updatedAt": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/meta/admin/config" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"paymentPlatform\": \"stripe\",
  \"paymentAdapterConfigs\": {
    \"key\": {
      \"key\": \"string\"
    }
  },
  \"hyperswitchMode\": \"sandbox\",
  \"giftFundingMode\": \"wallet\",
  \"giftsEnabled\": true,
  \"minAppVersionIos\": \"string\",
  \"minAppVersionAndroid\": \"string\",
  \"latestAppVersionIos\": \"string\",
  \"latestAppVersionAndroid\": \"string\",
  \"iosAppLink\": \"https://example.com/resource\",
  \"androidAppLink\": \"https://example.com/resource\"
}"
```

## PATCH `/meta/admin/latest-app-version`

Bump latest app version

Sets the latest published app version for iOS and/or Android. Intended for machine-to-machine use from the mobile release pipeline via an API key scoped to the meta.app_version.update feature.

Requires features: meta.app_version.update

**Tags:** Meta

**Requires authentication.**

**Features:** meta.app_version.update

### Request Body

Content-Type: `application/json`

```json
{
  "ios": "1.2.3",
  "android": "1.2.3"
}
```

### Responses

**200** – Updated latest app version

Content-Type: `application/json`

```json
{
  "latestAppVersion": {
    "ios": "string",
    "android": "string"
  },
  "updatedAt": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not initialized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PATCH "https://admin-dev.getcovo.com/api/meta/admin/latest-app-version" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## GET `/meta/config`

Public bootstrap config for mobile app

Returns the active payment platform, the publishable keys for both Stripe and HyperSwitch, the gift funding source, whether gifting is enabled at all, and minimum app versions. Clients initialise the SDK named by paymentPlatform and render the gifting flow named by giftFundingMode. When `giftsEnabled` is false, clients SHOULD hide the gifting entry point: creating a gift returns 503 while gifts that already exist stay claimable and refundable. Unauthenticated; cached for 60 seconds.

**Tags:** Meta

### Responses

**200** – App bootstrap config

Content-Type: `application/json`

```json
{
  "stripePublishableKey": "string",
  "paymentPlatform": "stripe",
  "hyperswitchPublishableKey": "string",
  "hyperswitchProfileId": "string",
  "giftFundingMode": "wallet",
  "giftsEnabled": true,
  "minAppVersion": {
    "ios": "string",
    "android": "string"
  },
  "latestAppVersion": {
    "ios": "string",
    "android": "string"
  },
  "appLink": {
    "ios": "string",
    "android": "string"
  }
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/meta/config" \
  -H "Accept: application/json"
```

## GET `/notifications`

List notifications

Returns a paginated collection of notifications.

**Tags:** Notifications

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Optional |
| type | query | any | Optional |
| severity | query | any | Optional |
| sourceEntityType | query | any | Optional |
| sourceEntityId | query | any | Optional |
| since | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated notifications

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "type": "string",
      "title": "string",
      "body": null,
      "titleKey": null,
      "bodyKey": null,
      "titleVariables": null,
      "bodyVariables": null,
      "icon": null,
      "severity": "string",
      "status": "string",
      "actions": [
        {
          "id": "string",
          "label": "string"
        }
      ],
      "sourceModule": null,
      "sourceEntityType": null,
      "sourceEntityId": null,
      "linkHref": null,
      "createdAt": "string",
      "readAt": null,
      "actionTaken": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/notifications?page=1&pageSize=20" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/notifications`

Create notification

Creates a notification for a user.

Requires features: notifications.create

**Tags:** Notifications

**Requires authentication.**

**Features:** notifications.create

### Request Body

Content-Type: `application/json`

```json
{
  "type": "string",
  "severity": "info",
  "recipientUserId": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**201** – Notification created

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/notifications" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"type\": \"string\",
  \"severity\": \"info\",
  \"recipientUserId\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## POST `/notifications/{id}/action`

POST /notifications/{id}/action

**Tags:** Notifications

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/notifications/:id/action" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/notifications/{id}/dismiss`

PUT /notifications/{id}/dismiss

**Tags:** Notifications

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/notifications/:id/dismiss" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/notifications/{id}/read`

PUT /notifications/{id}/read

**Tags:** Notifications

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/notifications/:id/read" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/notifications/{id}/restore`

PUT /notifications/{id}/restore

**Tags:** Notifications

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/notifications/:id/restore" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/notifications/batch`

POST /notifications/batch

Requires features: notifications.create

**Tags:** Notifications

**Requires authentication.**

**Features:** notifications.create

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/notifications/batch" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/notifications/feature`

POST /notifications/feature

Requires features: notifications.create

**Tags:** Notifications

**Requires authentication.**

**Features:** notifications.create

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/notifications/feature" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/notifications/mark-all-read`

PUT /notifications/mark-all-read

**Tags:** Notifications

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/notifications/mark-all-read" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/notifications/role`

POST /notifications/role

Requires features: notifications.create

**Tags:** Notifications

**Requires authentication.**

**Features:** notifications.create

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/notifications/role" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/notifications/settings`

GET /notifications/settings

Requires features: notifications.manage

**Tags:** Notifications

**Requires authentication.**

**Features:** notifications.manage

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/notifications/settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/notifications/settings`

POST /notifications/settings

Requires features: notifications.manage

**Tags:** Notifications

**Requires authentication.**

**Features:** notifications.manage

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/notifications/settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/notifications/unread-count`

GET /notifications/unread-count

**Tags:** Notifications

**Requires authentication.**

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/notifications/unread-count" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/payments/admin/bonus-tiers`

List the configured volume-bonus tiers

Requires features: payments.admin.bonus_tiers

**Tags:** Payments Admin

**Requires authentication.**

**Features:** payments.admin.bonus_tiers

### Responses

**200** – Bonus tiers

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "thresholdEuroCents": 1,
      "percent": 1,
      "tag": null,
      "sortOrder": 1,
      "isActive": true
    }
  ]
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/admin/bonus-tiers" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/payments/admin/bonus-tiers`

Create a volume-bonus tier

Creates a global bonus tier (threshold/percent/tag). Logged with an after snapshot for undo. Editing tiers does not retroactively change already-credited bonuses; the localized tier display re-prices on the next read.

Requires features: payments.admin.bonus_tiers

**Tags:** Payments Admin

**Requires authentication.**

**Features:** payments.admin.bonus_tiers

### Request Body

Content-Type: `application/json`

```json
{
  "thresholdEuroCents": 1,
  "percent": 1,
  "tag": null
}
```

### Responses

**201** – Created bonus tier

Content-Type: `application/json`

```json
{
  "tier": {
    "id": "00000000-0000-4000-8000-000000000000",
    "thresholdEuroCents": 1,
    "percent": 1,
    "tag": null,
    "sortOrder": 1,
    "isActive": true
  }
}
```

**400** – Invalid body

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Threshold already exists

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/admin/bonus-tiers" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"thresholdEuroCents\": 1,
  \"percent\": 1,
  \"tag\": null
}"
```

## DELETE `/payments/admin/bonus-tiers/{id}`

Delete a volume-bonus tier

Deletes a bonus tier. Logged with a before snapshot for undo (re-creates the row). The localized tier display re-prices on the next read.

Requires features: payments.admin.bonus_tiers

**Tags:** Payments Admin

**Requires authentication.**

**Features:** payments.admin.bonus_tiers

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Deleted bonus tier id

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/payments/admin/bonus-tiers/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/payments/admin/bonus-tiers/{id}`

Update a volume-bonus tier

Partially updates a bonus tier (threshold/percent/tag/sortOrder/isActive). Logged with before/after snapshots for undo. The localized tier display re-prices on the next read.

Requires features: payments.admin.bonus_tiers

**Tags:** Payments Admin

**Requires authentication.**

**Features:** payments.admin.bonus_tiers

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "tag": null
}
```

### Responses

**200** – Updated bonus tier

Content-Type: `application/json`

```json
{
  "tier": {
    "id": "00000000-0000-4000-8000-000000000000",
    "thresholdEuroCents": 1,
    "percent": 1,
    "tag": null,
    "sortOrder": 1,
    "isActive": true
  }
}
```

**400** – Invalid body or parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Threshold already exists

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/payments/admin/bonus-tiers/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"tag\": null
}"
```

## GET `/payments/admin/invoices`

List invoices (admin)

Returns Stripe invoices scoped to the admin caller's tenant/organization, newest first. Supports optional `status`, `userId`, and `search` filters plus `limit`/`offset` pagination. The `search` parameter is tokenized by whitespace (up to 5 tokens); each token must appear in `invoice_number`, `platform_invoice_id`, or anywhere in the `billing_snapshot` jsonb (user/billing/tax-rate values captured at invoice creation time). A token that is a UUID also matches the invoice id or user id.

Requires features: payments.admin.invoices.view

**Tags:** Payments Admin

**Requires authentication.**

**Features:** payments.admin.invoices.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Optional |
| userId | query | any | Optional |
| search | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – Admin invoice list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "platformInvoiceId": "string",
      "status": "draft",
      "amount": {
        "amountInMinorUnits": 1,
        "currency": "string"
      },
      "invoiceNumber": null,
      "hostedInvoiceUrl": null,
      "invoicePdfUrl": null,
      "reverseCharge": true,
      "createdAt": "string",
      "updatedAt": "string",
      "userId": "00000000-0000-4000-8000-000000000000",
      "tenantId": null,
      "organizationId": null,
      "taxRateId": null,
      "taxPercentage": null,
      "taxInclusive": null,
      "billingSnapshot": null
    }
  ],
  "total": 1
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load invoices

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/admin/invoices?limit=20&offset=0" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/payments/admin/invoices/{id}`

Get a single invoice (admin)

Returns a single invoice by id, scoped to the admin caller's tenant/organization. Includes the point-in-time `billingSnapshot` captured at invoice creation.

Requires features: payments.admin.invoices.view

**Tags:** Payments Admin

**Requires authentication.**

**Features:** payments.admin.invoices.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Invoice

Content-Type: `application/json`

```json
{
  "invoice": {
    "id": "00000000-0000-4000-8000-000000000000",
    "platformInvoiceId": "string",
    "status": "draft",
    "amount": {
      "amountInMinorUnits": 1,
      "currency": "string"
    },
    "invoiceNumber": null,
    "hostedInvoiceUrl": null,
    "invoicePdfUrl": null,
    "reverseCharge": true,
    "createdAt": "string",
    "updatedAt": "string",
    "userId": "00000000-0000-4000-8000-000000000000",
    "tenantId": null,
    "organizationId": null,
    "taxRateId": null,
    "taxPercentage": null,
    "taxInclusive": null,
    "billingSnapshot": null
  }
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Invoice not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load invoice

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/admin/invoices/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/payments/admin/invoices/{id}/payments`

List payments linked to an invoice (admin)

Returns all payments where `invoice_id` matches the given invoice id, scoped to the admin caller's tenant/organization. Capped at 50 results - in practice an invoice has at most a couple of linked payments (the initial PaymentIntent plus any retry).

Requires features: payments.admin.invoices.view

**Tags:** Payments Admin

**Requires authentication.**

**Features:** payments.admin.invoices.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Linked payments

Content-Type: `application/json`

```json
{
  "payments": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "walletId": "00000000-0000-4000-8000-000000000000",
      "userId": "00000000-0000-4000-8000-000000000000",
      "invoiceId": null,
      "status": "pending",
      "paymentType": "top_up",
      "amountEuroCents": null,
      "amountRefundedEuroCents": 1,
      "platformFeeEurCents": 1,
      "originalCurrency": "string",
      "originalAmountMinorUnits": 1,
      "platformPaymentId": "string",
      "description": "string",
      "failureReason": null,
      "refundedAt": null,
      "refundReason": null,
      "clientIp": null,
      "clientUserAgent": null,
      "clientDeviceId": null,
      "clientAppVersion": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "total": 1
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Invoice not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load linked payments

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/admin/invoices/00000000-0000-4000-8000-000000000000/payments" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/payments/admin/payments/{id}/refund`

Get refund context for a payment

Returns the payment summary, the maximum refundable amount (in the original payment currency, resolved from the payment platform) and the wallet transactions tied to this payment (the top-up credit, any refund debits, and the usage debits that drained funds from it).

Requires features: payments.admin.refund

**Tags:** Payments Admin

**Requires authentication.**

**Features:** payments.admin.refund

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Refund context

Content-Type: `application/json`

```json
{
  "payment": {
    "id": "00000000-0000-4000-8000-000000000000",
    "walletId": "00000000-0000-4000-8000-000000000000",
    "userId": "00000000-0000-4000-8000-000000000000",
    "status": "pending",
    "paymentType": "top_up",
    "amountEuroCents": null,
    "amountRefundedEuroCents": 1,
    "originalCurrency": "string",
    "originalAmountMinorUnits": 1,
    "platformPaymentId": "string",
    "description": "string",
    "createdAt": "string"
  },
  "maxRefundableMinorUnits": 1,
  "transactions": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "type": "string",
      "amountEuroCents": 1,
      "displayAmountMinorUnits": null,
      "displayCurrency": null,
      "description": "string",
      "referenceType": "string",
      "referenceId": null,
      "contributedEuroCents": null,
      "createdAt": "string"
    }
  ]
}
```

**404** – Payment not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/admin/payments/00000000-0000-4000-8000-000000000000/refund" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/payments/admin/payments/{id}/refund`

Create a refund for a payment

Creates a refund for the given amount (in the original payment currency minor units, from 0 up to the refundable amount). The wallet debit and payment status update are applied asynchronously by the charge.refunded webhook.

Requires features: payments.admin.refund

**Tags:** Payments Admin

**Requires authentication.**

**Features:** payments.admin.refund

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "amountMinorUnits": 1
}
```

### Responses

**200** – Refund created

Content-Type: `application/json`

```json
{
  "refundId": "string",
  "amountMinorUnits": 1,
  "currency": "string",
  "status": null
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Payment not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Refund rejected (payment not refundable, amount exceeds refundable, or the payment platform declined the refund).

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/admin/payments/00000000-0000-4000-8000-000000000000/refund" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"amountMinorUnits\": 1
}"
```

## GET `/payments/admin/settlement-records`

List processor settlement records

Read-only listing of Port B settlement records parsed from processor reports (Novalnet), including the reported gross/fee/net EUR figures and whether each row was matched to a payment. Gated on payments.settlement.view.

Requires features: payments.settlement.view

**Tags:** Payments Admin

**Requires authentication.**

**Features:** payments.settlement.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| processor | query | any | Optional |
| matched | query | any | Optional |

### Responses

**200** – Settlement records

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "processor": "string",
      "processorReference": "string",
      "paymentId": null,
      "grossEurCents": 1,
      "feeEurCents": 1,
      "netEurCents": 1,
      "fxRate": null,
      "payoutBatchRef": null,
      "valueDate": null,
      "appliedAt": null,
      "createdAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Load failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/admin/settlement-records?page=1&pageSize=50" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/payments/admin/workers/api-calls-retention`

Run api-calls-retention worker inline

Executes the payment API call retention sweep worker synchronously. Hard-deletes payment_api_calls rows older than 90 days. Used for integration tests and operational diagnostics. The trigger is recorded in the audit trail as `meta.background-job.run` against the calling admin, including runs that fail.

Requires features: payments.admin.run_retention

**Tags:** Payments

**Requires authentication.**

**Features:** payments.admin.run_retention

### Responses

**200** – Worker completed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**500** – Worker failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/admin/workers/api-calls-retention" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/payments/admin/workers/payment-device-trust-redaction`

Run payment-device-trust-redaction worker inline

Executes the payment device-trust redaction worker synchronously. Nulls client_ip, client_user_agent, client_device_id, and client_app_version on Payment rows for users whose account_deletion_waivers passed the § 195 BGB 3-year mark, keeping consent_version and financial columns. Used for integration tests and operational diagnostics. The trigger is recorded in the audit trail as `meta.background-job.run` against the calling admin, including runs that fail.

Requires features: payments.admin.run_retention

**Tags:** Payments

**Requires authentication.**

**Features:** payments.admin.run_retention

### Responses

**200** – Worker completed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**500** – Worker failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/admin/workers/payment-device-trust-redaction" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/payments/admin/workers/user-financial-data-retention`

Run user-financial-data-retention worker inline

Executes the user financial data retention purge worker synchronously. Hard-deletes Payment, Invoice, and PaymentCustomer rows for users past their account_deletion_waivers.purge_after date. Used for integration tests and operational diagnostics. The trigger is recorded in the audit trail as `meta.background-job.run` against the calling admin, including runs that fail.

Requires features: payments.admin.run_retention

**Tags:** Payments

**Requires authentication.**

**Features:** payments.admin.run_retention

### Responses

**200** – Worker completed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**500** – Worker failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/admin/workers/user-financial-data-retention" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/payments/admin/workers/webhook-events-retention`

Run webhook-events-retention worker inline

Executes the payment webhook event retention sweep worker synchronously. Hard-deletes terminal payment_webhook_events rows older than 1 year. Used for integration tests and operational diagnostics. The trigger is recorded in the audit trail as `meta.background-job.run` against the calling admin, including runs that fail.

Requires features: payments.admin.run_retention

**Tags:** Payments

**Requires authentication.**

**Features:** payments.admin.run_retention

### Responses

**200** – Worker completed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**500** – Worker failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/admin/workers/webhook-events-retention" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/payments/internal/test/hyperswitch-stub/payment-methods`

Seed a saved payment method into the in-memory HyperSwitch stub

Only available when OM_TEST_MODE=1. Returns 404 otherwise. Used by Playwright integration tests to set up a customer-attached HyperSwitch card (with recurringEnabled controlling off-session capability) before exercising the saved-methods flows.

**Tags:** Payments Internal

### Request Body

Content-Type: `application/json`

```json
{
  "customerId": "string"
}
```

### Responses

**200** – Seeded

Content-Type: `application/json`

```json
{
  "paymentMethodId": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Endpoint disabled (OM_TEST_MODE not set)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/internal/test/hyperswitch-stub/payment-methods" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"customerId\": \"string\"
}"
```

## POST `/payments/internal/test/hyperswitch-stub/payments`

Seed a settled payment (and prior refunds) into the in-memory HyperSwitch stub

Only available when OM_TEST_MODE=1. Returns 404 otherwise. Used by Playwright integration tests to give the refund-settlement read (payment retrieve + refunds.list) state to report before sending a refund webhook.

**Tags:** Payments Internal

### Request Body

Content-Type: `application/json`

```json
{
  "paymentId": "string",
  "amount": 1
}
```

### Responses

**200** – Seeded

Content-Type: `application/json`

```json
{
  "paymentId": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Endpoint disabled (OM_TEST_MODE not set)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/internal/test/hyperswitch-stub/payments" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"paymentId\": \"string\",
  \"amount\": 1
}"
```

## POST `/payments/internal/test/hyperswitch-stub/reset`

Clear all in-memory HyperSwitch stub state (payments + saved methods)

Only available when OM_TEST_MODE=1. Returns 404 otherwise. Used by Playwright integration tests to isolate stub state between cases.

**Tags:** Payments Internal

### Responses

**200** – Reset

Content-Type: `application/json`

```json
{
  "reset": true
}
```

**404** – Endpoint disabled (OM_TEST_MODE not set)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/internal/test/hyperswitch-stub/reset" \
  -H "Accept: application/json"
```

## POST `/payments/internal/test/stripe-stub/charges`

Seed a charge into the in-memory Stripe stub

Only available when OM_TEST_MODE=1. Returns 404 otherwise. Used by Playwright integration tests to set up the refundable charge backing a PaymentIntent before exercising the admin refund flow.

**Tags:** Payments Internal

### Request Body

Content-Type: `application/json`

```json
{
  "paymentIntentId": "string",
  "amount": 1,
  "net": null
}
```

### Responses

**200** – Seeded

Content-Type: `application/json`

```json
{
  "chargeId": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Endpoint disabled (OM_TEST_MODE not set)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/internal/test/stripe-stub/charges" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"paymentIntentId\": \"string\",
  \"amount\": 1,
  \"net\": null
}"
```

## GET `/payments/internal/test/stripe-stub/customers/{id}`

Return the in-memory Stripe stub customer default

Only available when OM_TEST_MODE=1. Returns 404 otherwise.

**Tags:** Payments Internal

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Customer default

Content-Type: `application/json`

```json
{
  "customerId": "string",
  "defaultPaymentMethodId": null
}
```

**404** – Endpoint disabled (OM_TEST_MODE not set)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/internal/test/stripe-stub/customers/string" \
  -H "Accept: application/json"
```

## POST `/payments/internal/test/stripe-stub/payment-methods`

Seed a payment method into the in-memory Stripe stub

Only available when OM_TEST_MODE=1. Returns 404 otherwise. Used by Playwright integration tests to set up customer-attached payment methods before exercising auto-refill or admin listing flows.

**Tags:** Payments Internal

### Request Body

Content-Type: `application/json`

```json
{
  "customer": "string"
}
```

### Responses

**200** – Seeded

Content-Type: `application/json`

```json
{
  "stripePaymentMethodId": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Endpoint disabled (OM_TEST_MODE not set)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/internal/test/stripe-stub/payment-methods" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"customer\": \"string\"
}"
```

## DELETE `/payments/internal/test/stripe-stub/payment-methods/{id}`

Remove a payment method from the in-memory Stripe stub

Only available when OM_TEST_MODE=1. Returns 404 otherwise.

**Tags:** Payments Internal

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**404** – Endpoint disabled (OM_TEST_MODE not set)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/payments/internal/test/stripe-stub/payment-methods/string" \
  -H "Accept: application/json"
```

## GET `/payments/me/bonus-tiers`

Get the volume-bonus tier ladder

Returns the canonical EUR bonus tiers (thresholdEuroCents, percent, marketing tag). Non-EUR users additionally get per-tier display-currency variants: displayThreshold (the localized, hysteresis-dampened threshold, smart-rounded), displayBonusAtThreshold (the localized bonus earned at that threshold), and recommendedDisplay ('percent' or 'absolute', chosen by the localized threshold heuristic 100 × FX_rate × PLI from the user's home-country World Bank Price Level Index). All display fields are omitted for EUR users and on FX failure, so the canonical EUR fields are always present.

Requires features: payments.topup.create

**Tags:** Payments

**Requires authentication.**

**Features:** payments.topup.create

### Responses

**200** – Volume bonus tiers

Content-Type: `application/json`

```json
{
  "tiers": [
    {
      "thresholdEuroCents": 1,
      "percent": 1,
      "tag": null
    }
  ]
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/me/bonus-tiers" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/payments/me/credit-notes/{id}/download`

Download credit note PDF

Redirects to the authenticated user's credit note PDF. For a historical Stripe-mirror credit note this is a 302 to the Stripe-hosted PDF; for an in-house invoicing credit note it is a 307 to `/api/invoicing/me/invoices/{id}/download` (rendered on demand). Scoped to `user_id = auth.sub` and the caller's tenant/organization. Returns 404 when the credit note does not belong to the caller or the PDF has not been finalised yet.

Requires features: payments.invoices.view

**Tags:** Payments

**Requires authentication.**

**Features:** payments.invoices.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

**302** – Redirect to the Stripe-hosted credit note PDF

Content-Type: `application/json`

**307** – Redirect to the in-house invoicing PDF download route

Content-Type: `application/json`

**400** – Invalid id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Credit note or PDF not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load credit note

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/me/credit-notes/00000000-0000-4000-8000-000000000000/download" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/payments/me/customer-session`

Create a Customer Session for managing saved payment methods

Creates a session for managing the caller's saved cards, routed by the customer's stored payment platform (not the active toggle). `paymentPlatform` names the platform the client must initialise and `platformEnvironment` its sandbox/production merchant account. On Stripe this is a CustomerSession with the customer_sheet component enabled: `clientSecret` backs the Stripe Customer Sheet, `publishableKey` is the Stripe key and `sdkAuthorization`/`profileId` are null. On HyperSwitch this is a Payment Methods Management (PMM) session: `clientSecret` is the PMM `cs_…` and `sdkAuthorization` (which also embeds the publishable key and profile id, echoed alongside) backs the PMM widget. `publishableKey` is present on both platforms; prefer it over the platform-wide meta/config value. The response is 200 in both cases.

Requires features: payments.method.manage

**Tags:** Payments

**Requires authentication.**

**Features:** payments.method.manage

### Responses

**200** – Session created. On Stripe `clientSecret` backs the Customer Sheet; on HyperSwitch `sdkAuthorization` backs the PMM widget. Fields not applicable to the customer's platform are null.

Content-Type: `application/json`

```json
{
  "clientSecret": null,
  "customerId": "string",
  "paymentPlatform": "stripe",
  "platformEnvironment": "sandbox",
  "sdkAuthorization": null,
  "publishableKey": null,
  "profileId": null
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/me/customer-session" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/payments/me/invoices`

List the caller's invoices

Returns all of the authenticated user's invoices, newest first: historical Stripe-mirror invoices and in-house invoicing documents (top-up receipts and monthly usage invoices) unioned into one list. In-house documents carry null `hostedInvoiceUrl`/`invoicePdfUrl` (their PDF is rendered on demand via the download endpoint) and their `platformInvoiceId` is the internal document id. When both a Stripe invoice and an in-house receipt exist for the same payment, the in-house document is returned. Scoped to `user_id = auth.sub` and the caller's tenant/organization. Supports optional `status` filter (draft, open, paid, uncollectible, void; in-house issued→open) and `limit`/`offset` pagination.

Requires features: payments.invoices.view

**Tags:** Payments

**Requires authentication.**

**Features:** payments.invoices.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – Caller's invoices

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "platformInvoiceId": "string",
      "status": "draft",
      "amount": {
        "amountInMinorUnits": 1,
        "currency": "string"
      },
      "invoiceNumber": null,
      "hostedInvoiceUrl": null,
      "invoicePdfUrl": null,
      "reverseCharge": true,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "total": 1
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load invoices

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/me/invoices?limit=20&offset=0" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/payments/me/invoices/{id}/download`

Download invoice PDF

Redirects to the authenticated user's invoice PDF. For a historical Stripe-mirror invoice this is a 302 to the Stripe-hosted PDF; for an in-house invoicing document it is a 307 to `/api/invoicing/me/invoices/{id}/download` (rendered on demand). Scoped to `user_id = auth.sub` and the caller's tenant/organization. Returns 404 when the invoice does not belong to the caller or the PDF has not been finalised yet.

Requires features: payments.invoices.view

**Tags:** Payments

**Requires authentication.**

**Features:** payments.invoices.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

**302** – Redirect to the Stripe-hosted invoice PDF

Content-Type: `application/json`

**307** – Redirect to the in-house invoicing PDF download route

Content-Type: `application/json`

**400** – Invalid id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Invoice or PDF not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load invoice

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/me/invoices/00000000-0000-4000-8000-000000000000/download" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/payments/me/payments/{id}`

Get payment status and details

Returns payment details for the authenticated user.

Requires features: payments.status.view

**Tags:** Payments

**Requires authentication.**

**Features:** payments.status.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Payment details

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "status": "pending",
  "amount": null,
  "originalAmount": {
    "amountInMinorUnits": 1,
    "currency": "string"
  },
  "paymentType": "top_up",
  "description": "string",
  "paymentPlatform": "stripe",
  "platformEnvironment": "sandbox",
  "failureReason": null,
  "fxBasis": null,
  "feeBasis": null,
  "createdAt": "string"
}
```

**400** – Invalid id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load payment

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/me/payments/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/payments/me/payments/methods`

List the caller's saved payment methods

Lists the authenticated user's saved cards, routed by the customer's stored payment platform (not the active toggle). `offSessionCapable` abstracts the platform's own reusability notion - on HyperSwitch a live mandate (`recurring_enabled`) - and is the field auto-refill gates on. Returns an empty list when the user has no payment customer yet. Returns 409 for customers on a platform that hosts its own manage-cards UI (Stripe): those clients must render the Customer Sheet via POST /me/customer-session instead.

Requires features: payments.method.manage

**Tags:** Payments

**Requires authentication.**

**Features:** payments.method.manage

### Responses

**200** – Saved payment methods

Content-Type: `application/json`

```json
{
  "paymentMethods": [
    {
      "id": "string",
      "brand": null,
      "last4": null,
      "expMonth": null,
      "expYear": null,
      "isDefault": true,
      "offSessionCapable": true
    }
  ],
  "paymentPlatform": null,
  "platformEnvironment": null
}
```

**409** – The customer's platform hosts its own manage-cards UI; use POST /me/customer-session

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/me/payments/methods" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/payments/me/payments/methods/{id}`

Remove one of the caller's saved payment methods

Detaches a saved card from the authenticated user's customer on the platform that owns it (routed by the customer's stored payment platform, not the active toggle). The method must belong to the caller; any other id returns 404. Returns 409 for customers on a platform that hosts its own manage-cards UI (Stripe), which removes cards through the Customer Sheet instead.

Requires features: payments.method.manage

**Tags:** Payments

**Requires authentication.**

**Features:** payments.method.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Payment method removed

Content-Type: `application/json`

```json
{
  "id": "string",
  "deleted": true
}
```

**404** – No such payment method for this user

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – The customer's platform hosts its own manage-cards UI; use the Customer Sheet

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/payments/me/payments/methods/string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/payments/me/payments/methods/{id}/default`

Promote one of the caller's saved cards to their default

Makes the given saved card the default for the authenticated user's customer, routed by the customer's stored payment platform (not the active toggle). Promoting the card that is already the default is a no-op and still returns 200. The method must belong to the caller; any other id returns 404. Returns 409 for customers on a platform that hosts its own manage-cards UI (Stripe), where the Customer Sheet sets the default.

Requires features: payments.method.manage

**Tags:** Payments

**Requires authentication.**

**Features:** payments.method.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Default payment method updated

Content-Type: `application/json`

```json
{
  "id": "string",
  "isDefault": true
}
```

**404** – No such payment method for this user

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – The customer's platform hosts its own manage-cards UI; use the Customer Sheet

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/me/payments/methods/string/default" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/payments/me/payments/methods/availability`

Whether the user has a usable off-session payment method

Returns whether the authenticated user has at least one reusable card (allow_redisplay='always') on their Stripe customer that is usable for off-session charges - i.e. not expired. Returns false when the user has no Stripe customer yet. Lets the mobile app decide whether to offer features that depend on off-session billing (auto-refill, network turbo).

Requires features: payments.method.manage

**Tags:** Payments

**Requires authentication.**

**Features:** payments.method.manage

### Responses

**200** – Off-session payment method availability

Content-Type: `application/json`

```json
{
  "hasOffSessionPaymentMethod": true
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/me/payments/methods/availability" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/payments/me/payments/top-up`

Create a PaymentIntent for wallet top-up

Creates a PaymentIntent through the payment platform this consumer is routed to (a plain charge; no Stripe Invoice is created - the customer-facing receipt is issued by the in-house invoicing module) and returns the client secret along with the customer id. `paymentPlatform` names the platform the client must initialise (`stripe` or `hyperswitch`) and `platformEnvironment` its sandbox/production merchant account - both are authoritative over the platform-wide `meta/config` values, which are public and cached and so cannot vary for a consumer on a per-consumer override. `publishableKey` (present on both platforms) and `profileId` (HyperSwitch business profile, null on Stripe) are the key/profile matching this response; prefer them over the `meta/config` pair. On Stripe it also returns a fresh CustomerSession client secret scoped to the `mobile_payment_element` component; on HyperSwitch `customerSessionClientSecret` is null (there is no CustomerSession object) and the client drives the sheet from `clientSecret` plus the `publishableKey`/`profileId`.

On Stripe the mobile app passes those three values to the Stripe Mobile SDK Payment Sheet (`paymentIntentClientSecret` + `customerId` + `customerSessionClientSecret`). Payment Sheet presents the saved cards already on the Stripe customer, lets the user add a new one with an optional "Save this card for future use" checkbox (enabled via the CustomerSession `mobile_payment_element.features.payment_method_save` flag — the mobile SDK only reads `mobile_payment_element`, not the web `payment_element` component), handles 3DS, Apple Pay and Google Pay, and confirms the PaymentIntent. When the user ticks the checkbox the new PaymentMethod is attached with `allow_redisplay=always` and the PaymentIntent gets `setup_future_usage` so it stays reusable for auto-refill. The backend does not pre-select a payment method, does not set `setup_future_usage` on the PaymentIntent directly, and does not charge off-session — all of that is delegated to Payment Sheet. Requires `stripe-react-native` >= 0.56.0 (CustomerSession support for Payment Sheet went GA there).

On HyperSwitch there is no CustomerSession, so the save-card capability is enabled from the intent: when the `payments.hyperswitch_save_card_on_top_up` feature toggle is on, the backend sets `setup_future_usage: off_session` on the payment. Unlike Stripe this does NOT force-save — on HyperSwitch the card is vaulted only when the mobile SDK sends `customer_acceptance` at confirm, which it does solely when the user ticks the save checkbox (`displaySavedPaymentMethodsCheckbox`). No tick means the card is charged once and not saved. The checkbox itself is rendered by the mobile SDK, not by this flag. See SPEC-070.

`amount.amountInMinorUnits` is expressed in the minor units of `amount.currency` (e.g. cents for EUR, kuruş for TRY). Minimum top-up is €5.00 and maximum is €5,000.00 (both compared against the EUR equivalent for non-EUR requests). When a bound is violated the 400 error message states the bound in the request currency (the localized, smart-rounded value from `GET /me/payments/top-up/config`), falling back to the EUR figure when the rate is unavailable. `idempotencyKey` is optional; reuse the same UUID to safely retry the same logical top-up.

`withdrawalConsent` is required and must be `true`: the user expressly agrees to the wallet credit before the expiry of the withdrawal period and acknowledges that, by doing so, they lose their statutory right of withdrawal. The request is rejected with 400 when it is missing or false. It is persisted on the Payment row as `withdrawal_consent` with `withdrawal_consent_source = top_up`.

When the internal-testing feature toggle `payments.internal_test_top_up_limit_eur_cents` is set to a positive value (0/null = disabled), the request is rejected with 400 if the user's lifetime credited top-ups (manual + auto-refill) plus this top-up would exceed that cap in EUR cents (e.g. 5000 = €50). An admin can exempt an individual consumer from that cap by setting `internalTestTopUpLimitExempt = true` on their profile (`PUT /api/consumers/admin`); for an exempt consumer this 400 never fires. The cap stays enforced for everyone else.

When `amount.currency` is a display currency that is no longer supported (deactivated for launch via the `fx.enabled_currency_codes` allowlist), the top-up is not rejected: the card is charged the EUR equivalent (presentment EUR) and the response carries a `currencyFallback` object ({ requestedCurrency, chargedCurrency: "EUR" }). The user keeps their display currency; only the charge switches to EUR. EUR itself is always supported.

Request example:
```json
{
  "amount": { "amountInMinorUnits": 500, "currency": "EUR" },
  "idempotencyKey": "00000000-0000-4000-8000-000000000000"
}
```

Chargeback-evidence headers (optional, persisted on the Payment row for 10-year financial retention and used to defend disputes after a possible account deletion):
- Client IP — resolved via the shared `getClientIp` helper, which respects `TRUST_PROXY_DEPTH`. With depth=0 (no trusted proxies) no proxy-supplied header is trusted and the IP is `null`; with depth≥1 it picks the appropriate entry from `X-Forwarded-For`, falling back to `X-Real-IP`. Deployments set depth=1. Stored as `client_ip`.
- `User-Agent` — stored as `client_user_agent`.
- `X-Covo-Device-Id` — stable per-install device id supplied by the mobile app; stored as `client_device_id`.
- `X-Covo-App-Version` — mobile app version; stored as `client_app_version`.

Requires features: payments.topup.create

**Tags:** Payments

**Requires authentication.**

**Features:** payments.topup.create

### Request Body

Content-Type: `application/json`

```json
{
  "amount": {
    "amountInMinorUnits": 1,
    "currency": "string"
  },
  "withdrawalConsent": true
}
```

### Responses

**200** – PaymentIntent created

Content-Type: `application/json`

```json
{
  "baseAmount": {
    "amountInMinorUnits": 1,
    "currency": "string"
  },
  "bonusAmount": {
    "amountInMinorUnits": 1,
    "currency": "string"
  },
  "bonusPercent": 1,
  "totalCredit": {
    "amountInMinorUnits": 1,
    "currency": "string"
  },
  "estimatedDisplayTotalCredit": {
    "amountInMinorUnits": 1,
    "currency": "string"
  },
  "clientSecret": "string",
  "paymentIntentId": "string",
  "customerId": "string",
  "paymentPlatform": "stripe",
  "platformEnvironment": "sandbox",
  "customerSessionClientSecret": null,
  "publishableKey": "string",
  "profileId": null
}
```

**400** – Validation error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**402** – Card charge failed before Payment Sheet could run (e.g. Stripe rejected the PaymentIntent creation). Body includes the Stripe failure message, decline code and PaymentIntent id when available.

Content-Type: `application/json`

```json
{
  "error": "string",
  "message": "string",
  "code": null,
  "paymentIntentId": null
}
```

**422** – Top-up cannot proceed because the consumer billing country is missing or no tax rate is configured for it. Body includes the reason and the rejected country (when known).

Content-Type: `application/json`

```json
{
  "error": "string",
  "reason": "billing-country-missing",
  "country": null
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/me/payments/top-up" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"amount\": {
    \"amountInMinorUnits\": 1,
    \"currency\": \"string\"
  },
  \"withdrawalConsent\": true
}"
```

## GET `/payments/me/payments/top-up/config`

Get the top-up min/max range and preset amounts

Returns the canonical EUR top-up bounds (rangeEuroCents: €5–€5,000) and EUR-anchored preset chips (presetsEuroCents). Non-EUR users additionally get display-currency variants: displayRange (the localized, hysteresis-dampened slider bounds — min rounded up, max rounded down) and displayPresets (the localized preset chips — smart-rounded to nearest and kept strictly increasing). All display fields are omitted for EUR users and on FX failure, so the canonical EUR values are always present.

Requires features: payments.topup.create

**Tags:** Payments

**Requires authentication.**

**Features:** payments.topup.create

### Responses

**200** – Top-up range and presets

Content-Type: `application/json`

```json
{
  "rangeEuroCents": {
    "minEuroCents": 1,
    "maxEuroCents": 1
  },
  "presetsEuroCents": [
    1
  ]
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/me/payments/top-up/config" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/payments/me/payments/top-up/preview`

Preview wallet top-up with volume bonus

Returns the volume bonus the user would receive for a given top-up amount. For non-EUR amounts, the EUR-equivalent is computed using the latest Stripe FX rate; the actual bonus on payment success uses the settled EUR amount and may differ by a few cents. When the requested currency is no longer supported (deactivated for launch) the preview is priced in EUR and includes a `currencyFallback` object ({ requestedCurrency, chargedCurrency: "EUR" }) - the user keeps their display currency but the card is charged in EUR.

Requires features: payments.topup.create

**Tags:** Payments

**Requires authentication.**

**Features:** payments.topup.create

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| amount | query | any | Required |
| currency | query | any | Required |

### Responses

**200** – Top-up preview

Content-Type: `application/json`

```json
{
  "baseAmount": {
    "amountInMinorUnits": 1,
    "currency": "string"
  },
  "bonusAmount": {
    "amountInMinorUnits": 1,
    "currency": "string"
  },
  "bonusPercent": 1,
  "totalCredit": {
    "amountInMinorUnits": 1,
    "currency": "string"
  },
  "estimatedDisplayTotalCredit": {
    "amountInMinorUnits": 1,
    "currency": "string"
  }
}
```

**400** – Validation error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/payments/me/payments/top-up/preview?amount=1&currency=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/payments/me/setup-intent`

Create a save-card session

Creates a save-card session (no charge) for setting up auto-refill, routed by the customer's stored payment platform (not the active toggle). `paymentPlatform` names the platform the client must initialise and `platformEnvironment` its sandbox/production merchant account. `clientSecret` + `publishableKey` (present on both platforms) drive the SDK; `profileId` is the HyperSwitch business profile (null on Stripe). Prefer these over the platform-wide meta/config values, which cannot vary for a consumer on a per-consumer override.

Requires features: payments.method.manage

**Tags:** Payments

**Requires authentication.**

**Features:** payments.method.manage

### Responses

**200** – SetupIntent created

Content-Type: `application/json`

```json
{
  "clientSecret": "string",
  "setupIntentId": "string",
  "paymentPlatform": "stripe",
  "platformEnvironment": "sandbox",
  "publishableKey": "string",
  "profileId": null
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/me/setup-intent" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/payments/webhooks/{platform}`

Receive payment platform webhook events

Public endpoint secured by the platform adapter signature verification (e.g. /webhooks/stripe, /webhooks/hyperswitch). Resolves the adapter by path segment, normalizes the event, claims it durably, logs an audit row, and emits an internal payments.webhook.* event for async processing by subscribers.

**Tags:** Webhooks

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| platform | path | any | Required |

### Responses

**200** – Event accepted (handled, duplicate, or unhandled)

Content-Type: `application/json`

**400** – Invalid signature or payload

Content-Type: `application/json`

**404** – Unknown payment platform

Content-Type: `application/json`

**413** – Payload too large

Content-Type: `application/json`

**500** – Processing failed

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/webhooks/stripe" \
  -H "Accept: application/json"
```

## POST `/payments/webhooks/settlement/{processor}`

Receive a processor settlement report

Public endpoint secured by Basic Authentication (per-processor credentials). Novalnet pushes its automated daily report here. Lands the raw body verbatim as an idempotent PaymentSettlementArtifact (unique per processor + sha256 of the body) before any parse, then enqueues async ingest. A re-delivered identical report is a no-op and still returns 200.

**Tags:** Webhooks

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| processor | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Report accepted (stored or duplicate)

Content-Type: `application/json`

```json
{
  "accepted": true,
  "duplicate": true
}
```

**400** – Body could not be read

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Authentication failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Unknown settlement processor

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**413** – Payload too large

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Processing failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/payments/webhooks/settlement/stripe" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## GET `/progress/active`

GET /progress/active

Requires features: progress.view

**Tags:** Progress

**Requires authentication.**

**Features:** progress.view

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/progress/active" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/progress/jobs`

List progressjobs

Returns a paginated collection of progressjobs scoped to the authenticated tenant.

Requires features: progress.view

**Tags:** Progress

**Requires authentication.**

**Features:** progress.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Optional |
| jobType | query | any | Optional |
| parentJobId | query | any | Optional |
| includeCompleted | query | any | Optional |
| completedSince | query | any | Optional |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| search | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated progressjobs

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "jobType": "string",
      "name": "string",
      "description": null,
      "status": "string",
      "progressPercent": 1,
      "processedCount": 1,
      "totalCount": null,
      "etaSeconds": null,
      "cancellable": true,
      "startedAt": null,
      "finishedAt": null,
      "errorMessage": null,
      "createdAt": null,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/progress/jobs?page=1&pageSize=20" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/progress/jobs`

Create progressjob

Creates a new progress job for tracking a long-running operation.

Requires features: progress.create

**Tags:** Progress

**Requires authentication.**

**Features:** progress.create

### Request Body

Content-Type: `application/json`

```json
{
  "jobType": "string",
  "name": "string",
  "cancellable": false
}
```

### Responses

**201** – ProgressJob created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/progress/jobs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"jobType\": \"string\",
  \"name\": \"string\",
  \"cancellable\": false
}"
```

## DELETE `/progress/jobs/{id}`

DELETE /progress/jobs/{id}

Requires features: progress.cancel

**Tags:** Progress

**Requires authentication.**

**Features:** progress.cancel

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**204** – Success

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/progress/jobs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/progress/jobs/{id}`

GET /progress/jobs/{id}

Requires features: progress.view

**Tags:** Progress

**Requires authentication.**

**Features:** progress.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/progress/jobs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/progress/jobs/{id}`

PUT /progress/jobs/{id}

Requires features: progress.update

**Tags:** Progress

**Requires authentication.**

**Features:** progress.update

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/progress/jobs/:id" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/query_index/purge`

Purge query index records

Queues a purge job to remove indexed records for an entity type within the active scope.

Requires features: query_index.purge

**Tags:** Query Index

**Requires authentication.**

**Features:** query_index.purge

### Request Body

Content-Type: `application/json`

```json
{
  "entityType": "string"
}
```

### Responses

**200** – Purge job accepted.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing entity type

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/query_index/purge" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityType\": \"string\"
}"
```

## POST `/query_index/reindex`

Trigger query index rebuild

Queues a reindex job for the specified entity type within the current tenant scope.

Requires features: query_index.reindex

**Tags:** Query Index

**Requires authentication.**

**Features:** query_index.reindex

### Request Body

Content-Type: `application/json`

```json
{
  "entityType": "string"
}
```

### Responses

**200** – Reindex job accepted.

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing entity type

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/query_index/reindex" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"entityType\": \"string\"
}"
```

## GET `/query_index/status`

Inspect query index coverage

Returns entity counts comparing base tables with the query index along with the latest job status.

Requires features: query_index.status.view

**Tags:** Query Index

**Requires authentication.**

**Features:** query_index.status.view

### Responses

**200** – Current query index status.

Content-Type: `application/json`

```json
{
  "items": [
    {
      "entityId": "string",
      "label": "string",
      "baseCount": null,
      "indexCount": null,
      "vectorCount": null,
      "ok": true,
      "job": {
        "status": "idle",
        "startedAt": null,
        "finishedAt": null,
        "heartbeatAt": null,
        "processedCount": null,
        "totalCount": null,
        "scope": null
      }
    }
  ],
  "errors": [
    {
      "id": "string",
      "source": "string",
      "handler": "string",
      "entityType": null,
      "recordId": null,
      "tenantId": null,
      "organizationId": null,
      "message": "string",
      "stack": null,
      "payload": null,
      "occurredAt": "string"
    }
  ],
  "logs": [
    {
      "id": "string",
      "source": "string",
      "handler": "string",
      "level": "info",
      "entityType": null,
      "recordId": null,
      "tenantId": null,
      "organizationId": null,
      "message": "string",
      "details": null,
      "occurredAt": "string"
    }
  ]
}
```

**400** – Tenant or organization context required

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/query_index/status" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/savings/admin/wallets/{walletId}`

Get a wallet's lifetime savings

Admin variant of GET /api/savings/me. Returns the EUR locked into the lifetime savings counter for the given wallet, its display-currency and data-equivalent projections, and the EUR still pending in un-matured top-ups with its display-currency projection. Scoped to the caller's tenant/organization.

Requires features: savings.admin.view

**Tags:** Savings

**Requires authentication.**

**Features:** savings.admin.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| walletId | path | any | Required |

### Responses

**200** – Lifetime savings summary

Content-Type: `application/json`

```json
{
  "lifetime": {
    "amountInMinorUnits": 1,
    "currency": "string"
  },
  "lifetimeData": {
    "bytes": 1
  },
  "pending": {
    "amountInMinorUnits": 1,
    "currency": "string"
  },
  "maturesNextAt": null
}
```

**400** – Invalid wallet id

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Wallet not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/savings/admin/wallets/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/savings/admin/workers/mature-top-up-batches`

Run the top-up batch maturation worker inline

Executes the maturation worker synchronously in the request handler. Used for integration tests and operational diagnostics; the scheduler normally invokes this worker on a one-minute cron. Request body is ignored. The trigger is recorded in the audit trail as `meta.background-job.run` against the calling admin, including runs that fail.

Requires features: savings.admin.run_maturation

**Tags:** Savings

**Requires authentication.**

**Features:** savings.admin.run_maturation

### Responses

**200** – Worker completed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**500** – Worker failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/savings/admin/workers/mature-top-up-batches" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/savings/admin/workers/wallet-savings-retention`

Run wallet-savings-retention worker inline

Executes the wallet savings retention purge worker synchronously. Hard-deletes wallet_savings rows for users past their account_deletion_waivers.purge_after date. Used for integration tests and operational diagnostics. The trigger is recorded in the audit trail as `meta.background-job.run` against the calling admin, including runs that fail.

Requires features: savings.admin.run_retention

**Tags:** Savings

**Requires authentication.**

**Features:** savings.admin.run_retention

### Responses

**200** – Worker completed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**500** – Worker failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/savings/admin/workers/wallet-savings-retention" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/savings/me`

Get the caller's lifetime savings

Returns the EUR locked into the lifetime savings counter by matured top-up batches, its display-currency and data-equivalent projections, and the EUR still pending in un-matured top-ups with its display-currency projection. Scoped to the caller's standard wallet.

Requires features: savings.view

**Tags:** Savings

**Requires authentication.**

**Features:** savings.view

### Responses

**200** – Lifetime savings summary

Content-Type: `application/json`

```json
{
  "lifetime": {
    "amountInMinorUnits": 1,
    "currency": "string"
  },
  "lifetimeData": {
    "bytes": 1
  },
  "pending": {
    "amountInMinorUnits": 1,
    "currency": "string"
  },
  "maturesNextAt": null
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/savings/me" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/scheduler/jobs`

Delete scheduledjob

Deletes a scheduled job by ID.

Requires features: scheduler.jobs.manage

**Tags:** Scheduler

**Requires authentication.**

**Features:** scheduler.jobs.manage

### Request Body

Content-Type: `application/json`

```json
{
  "id": "string"
}
```

### Responses

**200** – ScheduledJob deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/scheduler/jobs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\"
}"
```

## GET `/scheduler/jobs`

List scheduledjobs

Returns a paginated collection of scheduledjobs scoped to the authenticated organization.

Requires features: scheduler.jobs.view

**Tags:** Scheduler

**Requires authentication.**

**Features:** scheduler.jobs.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| id | query | any | Optional |
| search | query | any | Optional |
| scopeType | query | any | Optional |
| isEnabled | query | any | Required |
| sourceType | query | any | Optional |
| sourceModule | query | any | Optional |
| sort | query | any | Optional |
| order | query | any | Optional |
| ids | query | any | Optional. Comma-separated list of record UUIDs to filter by (max 200). |

### Responses

**200** – Paginated scheduledjobs

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "string",
      "description": null,
      "scopeType": "system",
      "organizationId": null,
      "tenantId": null,
      "scheduleType": "cron",
      "scheduleValue": "string",
      "timezone": "string",
      "targetType": "queue",
      "targetQueue": null,
      "targetCommand": null,
      "targetPayload": null,
      "requireFeature": null,
      "isEnabled": true,
      "lastRunAt": null,
      "nextRunAt": null,
      "sourceType": "user",
      "sourceModule": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/scheduler/jobs?page=1&pageSize=20" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/scheduler/jobs`

Create scheduledjob

Creates a new scheduled job with cron or interval-based scheduling.

Requires features: scheduler.jobs.manage

**Tags:** Scheduler

**Requires authentication.**

**Features:** scheduler.jobs.manage

### Request Body

Content-Type: `application/json`

```json
{
  "name": "string",
  "description": null,
  "scopeType": "system",
  "organizationId": null,
  "tenantId": null,
  "scheduleType": "cron",
  "scheduleValue": "string",
  "timezone": "UTC",
  "targetType": "queue",
  "targetQueue": null,
  "targetCommand": null,
  "targetPayload": null,
  "requireFeature": null,
  "isEnabled": true,
  "sourceType": "user",
  "sourceModule": null
}
```

### Responses

**201** – ScheduledJob created

Content-Type: `application/json`

```json
{
  "id": null
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/scheduler/jobs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"name\": \"string\",
  \"description\": null,
  \"scopeType\": \"system\",
  \"organizationId\": null,
  \"tenantId\": null,
  \"scheduleType\": \"cron\",
  \"scheduleValue\": \"string\",
  \"timezone\": \"UTC\",
  \"targetType\": \"queue\",
  \"targetQueue\": null,
  \"targetCommand\": null,
  \"targetPayload\": null,
  \"requireFeature\": null,
  \"isEnabled\": true,
  \"sourceType\": \"user\",
  \"sourceModule\": null
}"
```

## PUT `/scheduler/jobs`

Update scheduledjob

Updates an existing scheduled job by ID.

Requires features: scheduler.jobs.manage

**Tags:** Scheduler

**Requires authentication.**

**Features:** scheduler.jobs.manage

### Request Body

Content-Type: `application/json`

```json
{
  "id": "string",
  "description": null,
  "targetQueue": null,
  "targetCommand": null,
  "targetPayload": null,
  "requireFeature": null
}
```

### Responses

**200** – ScheduledJob updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/scheduler/jobs" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\",
  \"description\": null,
  \"targetQueue\": null,
  \"targetCommand\": null,
  \"targetPayload\": null,
  \"requireFeature\": null
}"
```

## GET `/scheduler/jobs/{id}/executions`

Get execution history for a schedule

Fetch recent executions from BullMQ for a scheduled job. Requires QUEUE_STRATEGY=async.

**Tags:** Scheduler

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| pageSize | query | any | Optional |

### Responses

**200** – Execution history

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "scheduleId": "00000000-0000-4000-8000-000000000000",
      "startedAt": "string",
      "finishedAt": null,
      "status": "running",
      "triggerType": "scheduled",
      "triggeredByUserId": null,
      "errorMessage": null,
      "errorStack": null,
      "durationMs": null,
      "queueJobId": "string",
      "queueName": "string",
      "attemptsMade": 1,
      "result": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1
}
```

**400** – Local strategy not supported

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Schedule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/scheduler/jobs/:id/executions?pageSize=20" \
  -H "Accept: application/json"
```

## GET `/scheduler/queue-jobs/{jobId}`

Get BullMQ job details and logs

Fetch detailed information and logs for a queue job. Requires QUEUE_STRATEGY=async.

**Tags:** Scheduler

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| jobId | path | any | Required |
| queue | query | any | Required |

### Responses

**200** – Job details and logs

Content-Type: `application/json`

```json
{
  "id": "string",
  "name": "string",
  "state": "waiting",
  "progress": null,
  "returnvalue": null,
  "failedReason": null,
  "stacktrace": null,
  "attemptsMade": 1,
  "processedOn": null,
  "finishedOn": null,
  "logs": [
    "string"
  ]
}
```

**400** – Invalid request or local strategy not supported

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Job not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/scheduler/queue-jobs/:jobId?queue=string" \
  -H "Accept: application/json"
```

## GET `/scheduler/targets`

List available queues and commands

Returns all registered queue names (from module workers) and command IDs (from the command registry) that can be used as schedule targets.

**Tags:** Scheduler

### Responses

**200** – Available targets

Content-Type: `application/json`

```json
{
  "queues": [
    {
      "value": "string",
      "label": "string"
    }
  ],
  "commands": [
    {
      "value": "string",
      "label": "string"
    }
  ]
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/scheduler/targets" \
  -H "Accept: application/json"
```

## POST `/scheduler/trigger`

Manually trigger a schedule

Executes a scheduled job immediately, bypassing the scheduled time. Only works with async queue strategy.

**Tags:** Scheduler

### Request Body

Content-Type: `application/json`

```json
{
  "id": "string"
}
```

### Responses

**200** – Schedule triggered successfully

Content-Type: `application/json`

```json
{
  "ok": true,
  "jobId": "string",
  "message": "string"
}
```

**400** – Invalid request or local strategy not supported

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Access denied

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Schedule not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/scheduler/trigger" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"string\"
}"
```

## GET `/search/embeddings`

Get embeddings configuration

Returns current embedding provider and model configuration.

Requires features: search.embeddings.view

**Tags:** Search

**Requires authentication.**

**Features:** search.embeddings.view

### Responses

**200** – Embeddings settings

Content-Type: `application/json`

```json
{
  "settings": {
    "openaiConfigured": true,
    "autoIndexingEnabled": true,
    "autoIndexingLocked": true,
    "lockReason": null,
    "embeddingConfig": null,
    "configuredProviders": [
      "openai"
    ],
    "indexedDimension": null,
    "reindexRequired": true,
    "documentCount": null
  }
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/search/embeddings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/search/embeddings`

Update embeddings configuration

Updates the embedding provider and model settings.

Requires features: search.embeddings.manage

**Tags:** Search

**Requires authentication.**

**Features:** search.embeddings.manage

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Updated settings

Content-Type: `application/json`

```json
{
  "settings": {
    "openaiConfigured": true,
    "autoIndexingEnabled": true,
    "autoIndexingLocked": true,
    "lockReason": null,
    "embeddingConfig": null,
    "configuredProviders": [
      "openai"
    ],
    "indexedDimension": null,
    "reindexRequired": true,
    "documentCount": null
  }
}
```

**400** – Invalid request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Auto-indexing disabled via environment

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Update failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Configuration service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/search/embeddings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/search/embeddings/reindex`

Trigger vector reindex

Starts a vector embedding reindex operation.

Requires features: search.embeddings.manage

**Tags:** Search

**Requires authentication.**

**Features:** search.embeddings.manage

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Reindex result

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**409** – Reindex already in progress

Content-Type: `application/json`

```json
{
  "error": "string",
  "lock": {
    "type": "fulltext",
    "action": "string",
    "startedAt": "string",
    "elapsedMinutes": 1,
    "processedCount": null,
    "totalCount": null
  }
}
```

**500** – Reindex failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Search indexer unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/search/embeddings/reindex" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/search/embeddings/reindex/cancel`

Cancel vector reindex

Cancels an in-progress vector reindex operation.

Requires features: search.embeddings.manage

**Tags:** Search

**Requires authentication.**

**Features:** search.embeddings.manage

### Responses

**200** – Cancel result

Content-Type: `application/json`

```json
{
  "ok": true,
  "jobsRemoved": 1
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/search/embeddings/reindex/cancel" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/search/index`

Purge vector index

Purges entries from the vector search index. Requires confirmAll=true when purging all entities.

Requires features: search.embeddings.manage

**Tags:** Search

**Requires authentication.**

**Features:** search.embeddings.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Optional. Specific entity ID to purge (e.g., "customers:customer_person_profile", "catalog:catalog_product") |
| confirmAll | query | any | Optional. Required when purging all entities |

### Responses

**200** – Purge result

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Missing confirmAll parameter

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Purge failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Search indexer unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/search/index" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/search/index`

List vector index entries

Returns paginated list of entries in the vector search index.

Requires features: search.view

**Tags:** Search

**Requires authentication.**

**Features:** search.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| entityId | query | any | Optional. Filter by entity ID (e.g., "customers:customer_person_profile", "catalog:catalog_product") |
| limit | query | any | Optional. Maximum entries to return (default: 50, max: 200) |
| offset | query | any | Optional. Offset for pagination (default: 0) |

### Responses

**200** – Index entries

Content-Type: `application/json`

```json
{
  "entries": [
    {
      "id": "string",
      "entityId": "string",
      "recordId": "string",
      "tenantId": "string",
      "organizationId": null
    }
  ],
  "limit": 1,
  "offset": 1
}
```

**500** – Failed to fetch index

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Vector strategy unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/search/index" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/search/reindex`

Trigger fulltext reindex

Starts a fulltext (Meilisearch) reindex operation. Can clear, recreate, or fully reindex.

Requires features: search.reindex

**Tags:** Search

**Requires authentication.**

**Features:** search.reindex

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Reindex result

Content-Type: `application/json`

```json
{
  "ok": true,
  "action": "clear",
  "entityId": null
}
```

**409** – Reindex already in progress

Content-Type: `application/json`

```json
{
  "error": "string",
  "lock": {
    "type": "fulltext",
    "action": "string",
    "startedAt": "string",
    "elapsedMinutes": 1,
    "processedCount": null,
    "totalCount": null
  }
}
```

**500** – Reindex failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Search service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/search/reindex" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/search/reindex/cancel`

Cancel fulltext reindex

Cancels an in-progress fulltext reindex operation.

Requires features: search.reindex

**Tags:** Search

**Requires authentication.**

**Features:** search.reindex

### Responses

**200** – Cancel result

Content-Type: `application/json`

```json
{
  "ok": true,
  "jobsRemoved": 1
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/search/reindex/cancel" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/search/search`

Search across all indexed entities

Performs a search using configured strategies (fulltext, vector, tokens). Use for search playground.

Requires features: search.view

**Tags:** Search

**Requires authentication.**

**Features:** search.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| q | query | any | Required. Search query (required) |
| limit | query | any | Optional. Maximum results to return (default: 50, max: 100) |
| strategies | query | any | Optional. Comma-separated strategies to use: fulltext, vector, tokens (e.g., "fulltext,vector") |
| entityTypes | query | any | Optional. Comma-separated entity types to filter results (e.g., "customers:customer_person_profile,catalog:catalog_product,sales:sales_order") |

### Responses

**200** – Search results

Content-Type: `application/json`

```json
{
  "results": [
    {
      "entityId": "string",
      "recordId": "string",
      "score": 1,
      "source": "fulltext"
    }
  ],
  "strategiesUsed": [
    "fulltext"
  ],
  "timing": 1,
  "query": "string",
  "limit": 1
}
```

**400** – Missing query parameter

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Search failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Search service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/search/search?q=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/search/search/global`

Global search (Cmd+K)

Performs a global search using saved tenant strategies. Does NOT accept strategies from URL.

Requires features: search.view

**Tags:** Search

**Requires authentication.**

**Features:** search.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| q | query | any | Required. Search query (required) |
| limit | query | any | Optional. Maximum results to return (default: 50, max: 100) |
| entityTypes | query | any | Optional. Comma-separated entity types to filter results (e.g., "customers:customer_person_profile,catalog:catalog_product,sales:sales_order") |

### Responses

**200** – Search results

Content-Type: `application/json`

```json
{
  "results": [
    {
      "entityId": "string",
      "recordId": "string",
      "score": 1,
      "source": "fulltext"
    }
  ],
  "strategiesUsed": [
    "fulltext"
  ],
  "strategiesEnabled": [
    "fulltext"
  ],
  "timing": 1,
  "query": "string",
  "limit": 1
}
```

**400** – Missing query parameter

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Search failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**503** – Search service unavailable

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/search/search/global?q=string" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/search/settings`

Get search settings and status

Returns search module configuration, available strategies, and reindex lock status.

Requires features: search.view

**Tags:** Search

**Requires authentication.**

**Features:** search.view

### Responses

**200** – Search settings

Content-Type: `application/json`

```json
{
  "settings": {
    "strategies": [
      {
        "id": "string",
        "name": "string",
        "priority": 1,
        "available": true
      }
    ],
    "fulltextConfigured": true,
    "fulltextStats": null,
    "vectorConfigured": true,
    "tokensEnabled": true,
    "defaultStrategies": [
      "string"
    ],
    "reindexLock": null,
    "fulltextReindexLock": null,
    "vectorReindexLock": null
  }
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/search/settings" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/search/settings/fulltext`

Get fulltext search configuration

Returns Meilisearch configuration status and index statistics.

Requires features: search.view

**Tags:** Search

**Requires authentication.**

**Features:** search.view

### Responses

**200** – Fulltext settings

Content-Type: `application/json`

```json
{
  "driver": null,
  "configured": true,
  "envVars": {
    "MEILISEARCH_HOST": {
      "set": true,
      "hint": "string"
    },
    "MEILISEARCH_API_KEY": {
      "set": true,
      "hint": "string"
    }
  },
  "optionalEnvVars": {
    "MEILISEARCH_INDEX_PREFIX": {
      "set": true,
      "hint": "string"
    },
    "SEARCH_EXCLUDE_ENCRYPTED_FIELDS": {
      "set": true,
      "hint": "string"
    }
  }
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/search/settings/fulltext" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/search/settings/global-search`

Get global search strategies

Returns the enabled strategies for Cmd+K global search.

Requires features: search.view

**Tags:** Search

**Requires authentication.**

**Features:** search.view

### Responses

**200** – Global search settings

Content-Type: `application/json`

```json
{
  "enabledStrategies": [
    "fulltext"
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/search/settings/global-search" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/search/settings/global-search`

Update global search strategies

Sets which strategies are enabled for Cmd+K global search.

Requires features: search.manage

**Tags:** Search

**Requires authentication.**

**Features:** search.manage

### Request Body

Content-Type: `application/json`

```json
{
  "enabledStrategies": [
    "fulltext"
  ]
}
```

### Responses

**200** – Updated settings

Content-Type: `application/json`

```json
{
  "ok": true,
  "enabledStrategies": [
    "fulltext"
  ]
}
```

**400** – Invalid request

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/search/settings/global-search" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"enabledStrategies\": [
    \"fulltext\"
  ]
}"
```

## GET `/search/settings/vector-store`

Get vector store configuration

Returns vector store configuration status.

Requires features: search.view

**Tags:** Search

**Requires authentication.**

**Features:** search.view

### Responses

**200** – Vector store settings

Content-Type: `application/json`

```json
{
  "currentDriver": "pgvector",
  "configured": true,
  "drivers": [
    {
      "id": "pgvector",
      "name": "string",
      "configured": true,
      "implemented": true,
      "envVars": [
        {
          "name": "string",
          "set": true,
          "hint": "string"
        }
      ]
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/search/settings/vector-store" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/storage-providers/s3/delete`

Delete file from S3

**Tags:** S3-Compatible Storage

### Responses

**204** – Success

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/storage-providers/s3/delete" \
  -H "Accept: application/json"
```

## GET `/storage-providers/s3/download`

Download file from S3

**Tags:** S3-Compatible Storage

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/storage-providers/s3/download" \
  -H "Accept: application/json"
```

## GET `/storage-providers/s3/list`

List S3 objects

**Tags:** S3-Compatible Storage

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/storage-providers/s3/list" \
  -H "Accept: application/json"
```

## POST `/storage-providers/s3/signed-url`

Generate S3 pre-signed URL

**Tags:** S3-Compatible Storage

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/storage-providers/s3/signed-url" \
  -H "Accept: application/json"
```

## POST `/storage-providers/s3/upload`

Upload file to S3

**Tags:** S3-Compatible Storage

### Responses

**201** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/storage-providers/s3/upload" \
  -H "Accept: application/json"
```

## DELETE `/tax_rates/admin/tax-rates`

Delete tax rate

Soft-deletes a tax rate by `id` (passed as the `id` query param). Action is logged and reversible via undo.

Requires features: tax_rates.admin.delete

**Tags:** Tax Rates

**Requires authentication.**

**Features:** tax_rates.admin.delete

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Required |

### Responses

**200** – Tax rate deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Tax rate not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/tax_rates/admin/tax-rates?id=00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/tax_rates/admin/tax-rates`

List tax rates (admin)

Returns the tax rate catalog with pagination and search by id, country, subdivision, postal code pattern, display name, or jurisdiction.

Requires features: tax_rates.admin.list

**Tags:** Tax Rates

**Requires authentication.**

**Features:** tax_rates.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| search | query | any | Optional |

### Responses

**200** – Tax rate list

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "country": "string",
      "subdivision": null,
      "postalCodePattern": null,
      "percentage": 1,
      "displayName": "string",
      "jurisdiction": null,
      "validFrom": "string",
      "validTo": null,
      "inclusive": true,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/tax_rates/admin/tax-rates?page=1&pageSize=50&sortField=country&sortDir=asc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/tax_rates/admin/tax-rates`

Create tax rate

Creates a new tax rate in the catalog. Action is logged and reversible via undo (soft-deletes the new row).

Requires features: tax_rates.admin.create

**Tags:** Tax Rates

**Requires authentication.**

**Features:** tax_rates.admin.create

### Request Body

Content-Type: `application/json`

```json
{
  "country": "string",
  "subdivision": null,
  "postalCodePattern": null,
  "percentage": 1,
  "displayName": "string",
  "jurisdiction": null,
  "validFrom": "string",
  "validTo": null,
  "inclusive": false
}
```

### Responses

**201** – Tax rate created

Content-Type: `application/json`

```json
{
  "ok": true,
  "id": "string"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/tax_rates/admin/tax-rates" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"country\": \"string\",
  \"subdivision\": null,
  \"postalCodePattern\": null,
  \"percentage\": 1,
  \"displayName\": \"string\",
  \"jurisdiction\": null,
  \"validFrom\": \"string\",
  \"validTo\": null,
  \"inclusive\": false
}"
```

## PUT `/tax_rates/admin/tax-rates`

Update tax rate

Updates an existing tax rate identified by `id` in the body. At least one mutable field must be provided. Action is logged and reversible via undo.

Requires features: tax_rates.admin.update

**Tags:** Tax Rates

**Requires authentication.**

**Features:** tax_rates.admin.update

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "subdivision": null,
  "postalCodePattern": null,
  "jurisdiction": null,
  "validTo": null
}
```

### Responses

**200** – Tax rate updated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Tax rate not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/tax_rates/admin/tax-rates" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\",
  \"subdivision\": null,
  \"postalCodePattern\": null,
  \"jurisdiction\": null,
  \"validTo\": null
}"
```

## GET `/tax_rates/admin/tax-rates/{id}`

Get a tax rate by id (admin)

Returns a single tax rate by its UUID. Used by the admin edit page to load a row without scanning the full catalog.

Requires features: tax_rates.admin.update

**Tags:** Tax Rates

**Requires authentication.**

**Features:** tax_rates.admin.update

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Tax rate

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "country": "string",
  "subdivision": null,
  "postalCodePattern": null,
  "percentage": 1,
  "displayName": "string",
  "jurisdiction": null,
  "validFrom": "string",
  "validTo": null,
  "inclusive": true,
  "createdAt": "string",
  "updatedAt": "string"
}
```

**400** – Invalid parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Tax rate not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/tax_rates/admin/tax-rates/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/user_notifications/{id}/read`

Mark a notification as read

Marks a single in-app notification belonging to the authenticated user as read. Idempotent — returns ok even if the notification was already read. Notifications belonging to other users return 404.

**Tags:** User Notifications

**Requires authentication.**

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Notification marked as read

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string",
  "issues": []
}
```

**404** – Notification not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/user_notifications/00000000-0000-4000-8000-000000000000/read" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/user_notifications/admin/deliveries`

List push notification deliveries (admin)

Returns paginated delivery records with joined notification type, notification group (the prefix of `type` before the first dot), device platform and token suffix. Filter by status, category, notification `type` (exact), `group` (prefix match on type), userId, silent, and creation date range, or free-text search on the recipient by email or name.

Requires features: user_notifications.manage

**Tags:** User Notifications Admin

**Requires authentication.**

**Features:** user_notifications.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| status | query | any | Optional |
| category | query | any | Optional |
| type | query | any | Optional |
| group | query | any | Optional |
| userId | query | any | Optional |
| search | query | any | Optional |
| silent | query | any | Optional |
| createdFrom | query | any | Optional |
| createdTo | query | any | Optional |

### Responses

**200** – Paginated list of push deliveries

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "notificationId": "00000000-0000-4000-8000-000000000000",
      "notificationType": null,
      "notificationGroup": null,
      "tokenId": "00000000-0000-4000-8000-000000000000",
      "userId": "00000000-0000-4000-8000-000000000000",
      "category": null,
      "status": "pending",
      "error": null,
      "attempts": 1,
      "silent": true,
      "sentAt": null,
      "createdAt": "string",
      "updatedAt": "string",
      "devicePlatform": null,
      "deviceTokenSuffix": null,
      "deviceIsActive": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/user_notifications/admin/deliveries?page=1&pageSize=25&sortField=createdAt&sortDir=desc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/user_notifications/admin/deliveries/{id}`

Get a push delivery with joined notification and sibling deliveries

Returns the delivery, its upstream Notification (null for silent/data-only pushes that have no backing Notification row), NotificationType metadata and all deliveries that share the same notification (one per device).

Requires features: user_notifications.manage

**Tags:** User Notifications Admin

**Requires authentication.**

**Features:** user_notifications.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Push delivery detail payload

Content-Type: `application/json`

```json
{
  "delivery": {
    "id": "00000000-0000-4000-8000-000000000000",
    "tokenId": "00000000-0000-4000-8000-000000000000",
    "devicePlatform": null,
    "deviceTokenSuffix": null,
    "deviceIsActive": null,
    "status": "pending",
    "error": null,
    "attempts": 1,
    "silent": true,
    "sentAt": null,
    "createdAt": "string",
    "updatedAt": "string",
    "notificationId": "00000000-0000-4000-8000-000000000000",
    "userId": "00000000-0000-4000-8000-000000000000",
    "category": null
  },
  "notification": null,
  "notificationType": null,
  "siblingDeliveries": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "tokenId": "00000000-0000-4000-8000-000000000000",
      "devicePlatform": null,
      "deviceTokenSuffix": null,
      "deviceIsActive": null,
      "status": "pending",
      "error": null,
      "attempts": 1,
      "silent": true,
      "sentAt": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ]
}
```

**400** – Invalid ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Delivery not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/user_notifications/admin/deliveries/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/user_notifications/admin/notification-preferences/{userId}`

Get a user's notification preferences (admin)

Returns one preference row per registered notification type for the given user. Target user must belong to the caller tenant.

Requires features: user_notifications.manage_preferences

**Tags:** User Notifications Admin

**Requires authentication.**

**Features:** user_notifications.manage_preferences

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| userId | path | any | Required |

### Responses

**200** – Preferences

Content-Type: `application/json`

```json
{
  "preferences": [
    {
      "type": "string",
      "category": "string",
      "group": "string",
      "nonOptOut": true,
      "pushEnabled": true,
      "emailEnabled": true
    }
  ]
}
```

**400** – Invalid ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – User not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/user_notifications/admin/notification-preferences/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/user_notifications/admin/notification-preferences/{userId}`

Update a user's notification preferences (admin)

Batch update preferences for the given user. Routed through CommandBus for action logging. Non-opt-out types are server-enforced as enabled regardless of submitted values.

Requires features: user_notifications.manage_preferences

**Tags:** User Notifications Admin

**Requires authentication.**

**Features:** user_notifications.manage_preferences

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| userId | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "preferences": [
    {
      "type": "string",
      "pushEnabled": true,
      "emailEnabled": true
    }
  ]
}
```

### Responses

**200** – Updated preferences

Content-Type: `application/json`

```json
{
  "preferences": [
    {
      "type": "string",
      "category": "string",
      "group": "string",
      "nonOptOut": true,
      "pushEnabled": true,
      "emailEnabled": true
    }
  ]
}
```

**400** – Invalid input

Content-Type: `application/json`

```json
{
  "error": "string",
  "issues": []
}
```

**404** – User not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Update failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/user_notifications/admin/notification-preferences/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"preferences\": [
    {
      \"type\": \"string\",
      \"pushEnabled\": true,
      \"emailEnabled\": true
    }
  ]
}"
```

## GET `/user_notifications/admin/push-devices`

List registered push notification devices (admin)

Returns paginated device records with joined user email and display name. Filter by userId, platform, isActive, pushPermitted, and updated date range, or free-text search by user email/name or device id.

Requires features: user_notifications.manage

**Tags:** User Notifications Admin

**Requires authentication.**

**Features:** user_notifications.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| userId | query | any | Optional |
| search | query | any | Optional |
| platform | query | any | Optional |
| isActive | query | any | Optional |
| pushPermitted | query | any | Optional |
| updatedFrom | query | any | Optional |
| updatedTo | query | any | Optional |

### Responses

**200** – Paginated list of push devices

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "userId": "00000000-0000-4000-8000-000000000000",
      "deviceId": "string",
      "tokenSuffix": "string",
      "platform": "ios",
      "pushPermitted": true,
      "isActive": true,
      "createdAt": "string",
      "updatedAt": "string",
      "userEmail": null,
      "userName": null
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/user_notifications/admin/push-devices?page=1&pageSize=25&sortField=updatedAt&sortDir=desc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/user_notifications/admin/push-devices/{id}`

Get a push notification device with recent delivery history

Returns the device record (including full token), joined user identity (email + display name), and the most recent delivery attempts targeting this token (up to historyLimit).

Requires features: user_notifications.manage

**Tags:** User Notifications Admin

**Requires authentication.**

**Features:** user_notifications.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Push device detail payload

Content-Type: `application/json`

```json
{
  "device": {
    "id": "00000000-0000-4000-8000-000000000000",
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "userId": "00000000-0000-4000-8000-000000000000",
    "deviceId": "string",
    "token": "string",
    "tokenSuffix": "string",
    "platform": "ios",
    "pushPermitted": true,
    "isActive": true,
    "createdAt": "string",
    "updatedAt": "string",
    "userEmail": null,
    "userName": null
  },
  "recentDeliveries": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "notificationId": "00000000-0000-4000-8000-000000000000",
      "notificationType": null,
      "notificationGroup": null,
      "status": "pending",
      "error": null,
      "attempts": 1,
      "category": null,
      "sentAt": null,
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "historyLimit": 1
}
```

**400** – Invalid ID

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Device not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/user_notifications/admin/push-devices/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/user_notifications/admin/push-devices/deactivate`

Deactivate a push notification device (admin)

Sets `is_active = false` on a registered push device. Undoable via the action log.

Requires features: user_notifications.manage

**Tags:** User Notifications Admin

**Requires authentication.**

**Features:** user_notifications.manage

### Request Body

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**200** – Device deactivated (or was already inactive)

Content-Type: `application/json`

```json
{
  "alreadyInactive": true
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string",
  "issues": []
}
```

**404** – Device not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/user_notifications/admin/push-devices/deactivate" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"id\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## POST `/user_notifications/admin/send-custom`

Send a custom push notification (admin)

Sends a one-off push notification to a chosen user. `pushDeviceIds` is the list of push notification token row ids to target; clients pass a single id to target one device or all of the user's ids to fan out. The notification is also persisted to the user's in-app feed.

Requires features: user_notifications.send_custom

**Tags:** User Notifications Admin

**Requires authentication.**

**Features:** user_notifications.send_custom

### Request Body

Content-Type: `application/json`

```json
{
  "userId": "00000000-0000-4000-8000-000000000000",
  "pushDeviceIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "title": "string",
  "body": "string",
  "silent": false
}
```

### Responses

**200** – Notification persisted and dispatched

Content-Type: `application/json`

```json
{
  "notificationId": "00000000-0000-4000-8000-000000000000",
  "deliveries": [
    {
      "deviceId": "string",
      "pushDeviceId": "00000000-0000-4000-8000-000000000000",
      "status": "sent",
      "error": null
    }
  ],
  "sentCount": 1,
  "failedCount": 1,
  "pendingCount": 1
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string",
  "issues": []
}
```

**404** – Device not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/user_notifications/admin/send-custom" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"userId\": \"00000000-0000-4000-8000-000000000000\",
  \"pushDeviceIds\": [
    \"00000000-0000-4000-8000-000000000000\"
  ],
  \"title\": \"string\",
  \"body\": \"string\",
  \"silent\": false
}"
```

## PUT `/user_notifications/mark-all-read`

Mark all notifications as read

Bulk marks every unread in-app notification belonging to the authenticated user as read. Returns the number of notifications updated.

**Tags:** User Notifications

**Requires authentication.**

### Responses

**200** – Notifications marked as read

Content-Type: `application/json`

```json
{
  "ok": true,
  "count": 1
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/user_notifications/mark-all-read" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/user_notifications/notification-preferences`

Get notification preferences

Returns one preference row per registered notification type, excluding types flagged `hiddenFromSettings` (those still arrive normally and stay visible in the admin panel). Each entry includes the type, its category, a derived `group` (the prefix before the first dot in the type id), and the `nonOptOut` flag so the client can disable the toggle for forced-on types. Push and email flags default to enabled when no preference is saved.

**Tags:** User Notifications

**Requires authentication.**

### Responses

**200** – Preferences

Content-Type: `application/json`

```json
{
  "preferences": [
    {
      "type": "string",
      "category": "string",
      "group": "string",
      "nonOptOut": true,
      "pushEnabled": true,
      "emailEnabled": true
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/user_notifications/notification-preferences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/user_notifications/notification-preferences`

Update notification preferences

Batch update preferences. Only types known to the registry are accepted. Types marked as non-opt-out are enforced as enabled server-side regardless of submitted values.

**Tags:** User Notifications

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "preferences": [
    {
      "type": "string",
      "pushEnabled": true,
      "emailEnabled": true
    }
  ]
}
```

### Responses

**200** – Updated preferences

Content-Type: `application/json`

```json
{
  "preferences": [
    {
      "type": "string",
      "category": "string",
      "group": "string",
      "nonOptOut": true,
      "pushEnabled": true,
      "emailEnabled": true
    }
  ]
}
```

**400** – Invalid input

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/user_notifications/notification-preferences" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"preferences\": [
    {
      \"type\": \"string\",
      \"pushEnabled\": true,
      \"emailEnabled\": true
    }
  ]
}"
```

## GET `/user_notifications/notification-types`

Get notification types

Returns registered notification types with category, derived `group` (the raw prefix before the first dot in the type id, used by the client to key group icons), a localized `groupLabel` for that group, opt-out metadata, channels, priority, and localized `title` + `description` strings. Types flagged `hiddenFromSettings` are excluded so they do not clutter the screen (they still arrive normally and remain visible in the admin panel). Translations are resolved server-side using the request locale (cookie or `Accept-Language`); `groupLabel` falls back to a humanized form of the group key when no `user_notifications.groups.<key>` translation exists, while `title`/`description` are `null` for types that do not declare i18n keys (e.g., admin-only types). Used by the mobile app to build the notification settings screen.

**Tags:** User Notifications

**Requires authentication.**

### Responses

**200** – Notification types

Content-Type: `application/json`

```json
{
  "types": [
    {
      "type": "string",
      "category": "string",
      "group": "string",
      "groupLabel": "string",
      "nonOptOut": true,
      "silent": true,
      "channels": [
        "push"
      ],
      "priority": "critical",
      "title": null,
      "description": null
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/user_notifications/notification-types" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/user_notifications/push-notification-tokens`

Deregister device token

Deactivate device token on logout. Recorded in the audit trail as `user_notifications.push-device.deactivate`.

**Tags:** User Notifications

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "deviceId": "string"
}
```

### Responses

**200** – Token deactivated

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**400** – Invalid input

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/user_notifications/push-notification-tokens" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"deviceId\": \"string\"
}"
```

## POST `/user_notifications/push-notification-tokens`

Register device token

Register or update a device push notification token. Upserts by (tenantId, userId, deviceId). If the same deviceId is registered by another user, any existing active registration is deactivated. Recorded in the audit trail as `user_notifications.push-device.register` unless the call changes nothing.

**Tags:** User Notifications

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "deviceId": "string",
  "token": "string",
  "platform": "ios",
  "pushPermitted": true
}
```

### Responses

**200** – Token registered

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "deviceId": "string",
  "platform": "ios",
  "pushPermitted": true,
  "isActive": true,
  "locale": null,
  "createdAt": "string"
}
```

**400** – Invalid input

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Conflict

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/user_notifications/push-notification-tokens" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"deviceId\": \"string\",
  \"token\": \"string\",
  \"platform\": \"ios\",
  \"pushPermitted\": true
}"
```

## PUT `/user_notifications/push-notification-tokens`

Update device token

Update token value or push permission status for an existing device. Recorded in the audit trail as `user_notifications.push-device.update` when the token or push permission actually changes.

**Tags:** User Notifications

**Requires authentication.**

### Request Body

Content-Type: `application/json`

```json
{
  "deviceId": "string"
}
```

### Responses

**200** – Token updated

Content-Type: `application/json`

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "deviceId": "string",
  "platform": "ios",
  "pushPermitted": true,
  "isActive": true,
  "locale": null,
  "createdAt": "string"
}
```

**400** – Invalid input

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Token not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/user_notifications/push-notification-tokens" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"deviceId\": \"string\"
}"
```

## GET `/version`

Deployed Open Mercato version

**Tags:** API Documentation

### Responses

**200** – Success response

Content-Type: `application/json`

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/version" \
  -H "Accept: application/json"
```

## GET `/waitlist/admin`

List waitlist entries (admin)

Returns all waitlist entries with pagination, status filter, and search by email, first name or last name. When the query is a UUID it also matches the entry id and reference id.

Requires features: waitlist.view

**Tags:** Waitlist

**Requires authentication.**

**Features:** waitlist.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |
| status | query | any | Optional |
| search | query | any | Optional. Filter entries whose email contains this substring (case-insensitive) |

### Responses

**200** – Paginated list of waitlist entries

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "firstName": "string",
      "lastName": "string",
      "email": "string",
      "status": "pending",
      "referenceId": null,
      "createdAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/waitlist/admin?page=1&pageSize=25&sortField=createdAt&sortDir=desc" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## DELETE `/waitlist/admin/{id}`

Soft-delete a waitlist entry (admin)

Marks a waitlist entry as deleted so it can be restored later. Does not affect any invite code already issued.

Requires features: waitlist.manage

**Tags:** Waitlist

**Requires authentication.**

**Features:** waitlist.manage

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Entry soft-deleted

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**404** – Entry not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/waitlist/admin/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/waitlist/admin/grant`

Grant invite to waitlist entries (admin)

Generates an activation code for each pending entry and marks them as invited. Already-invited entries are skipped. Bulk-capable (up to 100 IDs).

Requires features: waitlist.manage

**Tags:** Waitlist

**Requires authentication.**

**Features:** waitlist.manage

### Request Body

Content-Type: `application/json`

```json
{
  "ids": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

### Responses

**200** – Grant result

Content-Type: `application/json`

```json
{
  "grantedIds": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "skippedIds": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – One or more entries not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/waitlist/admin/grant" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"ids\": [
    \"00000000-0000-4000-8000-000000000000\"
  ]
}"
```

## POST `/waitlist/signup`

Sign up for the waitlist

Adds an email to the waitlist. Idempotent — submitting the same email again returns the same response. Rate limited to 5 requests per 10 minutes per IP.

**Tags:** Waitlist

### Request Body

Content-Type: `application/json`

```json
{
  "firstName": "string",
  "lastName": "string",
  "email": "user@example.com"
}
```

### Responses

**200** – Added to waitlist (or already on it)

Content-Type: `application/json`

```json
{
  "status": "pending"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**429** – Rate limit exceeded

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/waitlist/signup" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"firstName\": \"string\",
  \"lastName\": \"string\",
  \"email\": \"user@example.com\"
}"
```

## GET `/wallet/admin/wallets`

List all wallets

Returns paginated list of all wallets with optional type filter. The search query matches the wallet owner by partial email (via the search token index) or by first/last name. When the query is a UUID it also matches the wallet id and owner user id. Each wallet also includes an optional displayBalance in the owner's display currency when that currency is set and differs from EUR.

Requires features: wallet.admin.list

**Tags:** Wallet Admin

**Requires authentication.**

**Features:** wallet.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | query | any | Optional |
| userId | query | any | Optional |
| search | query | any | Optional |
| type | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – Wallet list

Content-Type: `application/json`

```json
{
  "wallets": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "userId": "00000000-0000-4000-8000-000000000000",
      "userEmail": "string",
      "userName": null,
      "type": "standard",
      "balance": {
        "amountInMinorUnits": 1,
        "currency": "string"
      },
      "lastTransactionAt": null
    }
  ],
  "total": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/wallet/admin/wallets?limit=20&offset=0" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/wallet/admin/wallets`

Create wallets for user

Creates standard and bonus wallets for a user. Idempotent — returns existing wallets if already created.

Requires features: wallet.admin.create

**Tags:** Wallet Admin

**Requires authentication.**

**Features:** wallet.admin.create

### Request Body

Content-Type: `application/json`

```json
{
  "userId": "00000000-0000-4000-8000-000000000000"
}
```

### Responses

**201** – Wallets created

Content-Type: `application/json`

```json
{
  "standardId": "00000000-0000-4000-8000-000000000000",
  "bonusId": "00000000-0000-4000-8000-000000000000"
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/wallet/admin/wallets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"userId\": \"00000000-0000-4000-8000-000000000000\"
}"
```

## GET `/wallet/admin/wallets/{id}/payment-calls`

List payment-processor API calls related to a wallet

Returns paginated payment-processor API calls (outbound + inbound webhooks + admin actions) correlated to a specific wallet via direct wallet_id or via any payment owned by the wallet. For the standard wallet it additionally surfaces user-scoped calls (mandate setup, payment-method management) correlated only by the owner's user_id within the wallet's organization; bonus wallets omit those to avoid double-listing the user's shared calls.

Requires features: wallet.admin.list

**Tags:** Wallet Admin

**Requires authentication.**

**Features:** wallet.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| direction | query | any | Optional |
| handlingStatus | query | any | Optional |
| paymentPlatform | query | any | Optional |
| processor | query | any | Optional |

### Responses

**200** – Payment-processor API calls

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "paymentPlatform": "stripe",
      "processor": null,
      "direction": "outbound",
      "handlingStatus": null,
      "method": "string",
      "endpoint": "string",
      "eventType": null,
      "statusCode": null,
      "durationMs": null,
      "idempotencyKey": null,
      "externalEventId": null,
      "errorMessage": null,
      "requestId": null,
      "requestHeaders": null,
      "requestBody": null,
      "responseHeaders": null,
      "responseBody": null,
      "paymentId": null,
      "walletId": null,
      "userId": null,
      "platformPaymentId": null,
      "platformChargeId": null,
      "platformCustomerId": null,
      "platformSetupIntentId": null,
      "platformRefundId": null,
      "trigger": null,
      "triggers": null,
      "createdAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Wallet not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/wallet/admin/wallets/00000000-0000-4000-8000-000000000000/payment-calls?page=1&pageSize=25" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/wallet/admin/wallets/{id}/payment-methods`

List a wallet owner's saved Stripe payment methods

Reads the wallet's owner Stripe customer and lists the customer's card payment methods that are reusable off-session (allow_redisplay='always'). Marks the one set as invoice_settings.default_payment_method as the default. Returns an empty list when the user has no Stripe customer yet.

Requires features: wallet.admin.list

**Tags:** Wallet Admin

**Requires authentication.**

**Features:** wallet.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Payment methods

Content-Type: `application/json`

```json
{
  "methods": [
    {
      "stripePaymentMethodId": "string",
      "type": "string",
      "cardBrand": null,
      "cardLast4": null,
      "cardExpMonth": null,
      "cardExpYear": null,
      "isDefault": true
    }
  ]
}
```

**400** – Invalid path parameter (non-UUID wallet id)

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Wallet not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/wallet/admin/wallets/00000000-0000-4000-8000-000000000000/payment-methods" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/wallet/admin/wallets/{id}/payments`

List wallet payments and credit notes

Returns a paginated, date-sorted ledger merging Stripe-backed payments and issued credit notes for a specific wallet, each tagged with recordType. Credit notes are omitted when a payment-specific status/paymentType filter is active.

Requires features: wallet.admin.list

**Tags:** Wallet Admin

**Requires authentication.**

**Features:** wallet.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| status | query | any | Optional |
| paymentType | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – Payments and credit notes

Content-Type: `application/json`

```json
{
  "payments": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "walletId": "00000000-0000-4000-8000-000000000000",
      "userId": "00000000-0000-4000-8000-000000000000",
      "invoiceId": null,
      "status": "pending",
      "paymentType": "top_up",
      "amountEuroCents": null,
      "amountRefundedEuroCents": 1,
      "platformFeeEurCents": 1,
      "originalCurrency": "string",
      "originalAmountMinorUnits": 1,
      "platformPaymentId": "string",
      "description": "string",
      "failureReason": null,
      "refundedAt": null,
      "refundReason": null,
      "clientIp": null,
      "clientUserAgent": null,
      "clientDeviceId": null,
      "clientAppVersion": null,
      "createdAt": "string",
      "updatedAt": "string",
      "recordType": "payment"
    }
  ],
  "total": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Wallet not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/wallet/admin/wallets/00000000-0000-4000-8000-000000000000/payments?limit=20&offset=0" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/wallet/admin/wallets/{id}/payments/{paymentId}`

Get single wallet payment

Returns a single Stripe-backed payment by id, scoped to the given wallet.

Requires features: wallet.admin.list

**Tags:** Wallet Admin

**Requires authentication.**

**Features:** wallet.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| paymentId | path | any | Required |

### Responses

**200** – Payment

Content-Type: `application/json`

```json
{
  "payment": {
    "id": "00000000-0000-4000-8000-000000000000",
    "walletId": "00000000-0000-4000-8000-000000000000",
    "userId": "00000000-0000-4000-8000-000000000000",
    "invoiceId": null,
    "status": "pending",
    "paymentType": "top_up",
    "amountEuroCents": null,
    "amountRefundedEuroCents": 1,
    "platformFeeEurCents": 1,
    "originalCurrency": "string",
    "originalAmountMinorUnits": 1,
    "platformPaymentId": "string",
    "description": "string",
    "failureReason": null,
    "refundedAt": null,
    "refundReason": null,
    "clientIp": null,
    "clientUserAgent": null,
    "clientDeviceId": null,
    "clientAppVersion": null,
    "createdAt": "string",
    "updatedAt": "string"
  }
}
```

**404** – Wallet or payment not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/wallet/admin/wallets/00000000-0000-4000-8000-000000000000/payments/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/wallet/admin/wallets/{id}/payments/{paymentId}/payment-calls`

List payment-processor API calls related to a single payment

Returns paginated payment-processor API calls (outbound + inbound webhooks + admin actions) for a specific payment. Correlates via direct payment_id, or via platform_payment_id for webhooks logged before the payment record was linked.

Requires features: wallet.admin.list

**Tags:** Wallet Admin

**Requires authentication.**

**Features:** wallet.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| paymentId | path | any | Required |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| direction | query | any | Optional |
| handlingStatus | query | any | Optional |
| paymentPlatform | query | any | Optional |
| processor | query | any | Optional |

### Responses

**200** – Payment-processor API calls

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "paymentPlatform": "stripe",
      "processor": null,
      "direction": "outbound",
      "handlingStatus": null,
      "method": "string",
      "endpoint": "string",
      "eventType": null,
      "statusCode": null,
      "durationMs": null,
      "idempotencyKey": null,
      "externalEventId": null,
      "errorMessage": null,
      "requestId": null,
      "requestHeaders": null,
      "requestBody": null,
      "responseHeaders": null,
      "responseBody": null,
      "paymentId": null,
      "walletId": null,
      "userId": null,
      "platformPaymentId": null,
      "platformChargeId": null,
      "platformCustomerId": null,
      "platformSetupIntentId": null,
      "platformRefundId": null,
      "trigger": null,
      "triggers": null,
      "createdAt": "string"
    }
  ],
  "total": 1,
  "totalPages": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Wallet or payment not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/wallet/admin/wallets/00000000-0000-4000-8000-000000000000/payments/00000000-0000-4000-8000-000000000000/payment-calls?page=1&pageSize=25" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/wallet/admin/wallets/{id}/transactions`

List wallet transactions

Returns paginated list of transactions for a specific wallet.

Requires features: wallet.admin.list

**Tags:** Wallet Admin

**Requires authentication.**

**Features:** wallet.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| type | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – Transaction list

Content-Type: `application/json`

```json
{
  "transactions": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "type": "credit",
      "amount": {
        "amountInMinorUnits": 1,
        "currency": "string"
      },
      "description": "string",
      "referenceType": "top_up",
      "referenceId": null,
      "createdAt": "string"
    }
  ],
  "total": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Wallet not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/wallet/admin/wallets/00000000-0000-4000-8000-000000000000/transactions?limit=20&offset=0" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## POST `/wallet/admin/wallets/{id}/transactions`

Create manual wallet adjustment

Credits or debits a wallet manually for support/adjustment purposes.

Requires features: wallet.admin.adjust

**Tags:** Wallet Admin

**Requires authentication.**

**Features:** wallet.admin.adjust

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "type": "credit",
  "amountEuroCents": 1,
  "reason": "string"
}
```

### Responses

**200** – Transaction created

Content-Type: `application/json`

```json
{
  "transactionId": "00000000-0000-4000-8000-000000000000",
  "newBalance": {
    "amountInMinorUnits": 1,
    "currency": "string"
  }
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Wallet not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/wallet/admin/wallets/00000000-0000-4000-8000-000000000000/transactions" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"type\": \"credit\",
  \"amountEuroCents\": 1,
  \"reason\": \"string\"
}"
```

## POST `/wallet/admin/workers/wallet-retention`

Run wallet-retention worker inline

Executes the wallet retention purge worker synchronously. Hard-deletes wallets and their ledger (transactions, credit batches, idempotency keys, lifetime savings) for users past their account_deletion_waivers.purge_after date. Used for integration tests and operational diagnostics. The trigger is recorded in the audit trail as `meta.background-job.run` against the calling admin, including runs that fail.

Requires features: wallet.admin.run_retention

**Tags:** Wallet

**Requires authentication.**

**Features:** wallet.admin.run_retention

### Responses

**200** – Worker completed

Content-Type: `application/json`

```json
{
  "ok": true
}
```

**500** – Worker failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/wallet/admin/workers/wallet-retention" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/wallet/dashboards/recent-activity`

Recent activity

Returns the latest mixed-stream of wallet top-ups, spend, refunds, and eSIM activations scoped to the caller's tenant/organization. User labels are first-name + last-initial for privacy.

Requires features: analytics.view, wallet.admin.list

**Tags:** Wallet Dashboards

**Requires authentication.**

**Features:** analytics.view, wallet.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| limit | query | any | Optional |

### Responses

**200** – Recent activity items

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "kind": "top_up",
      "occurredAt": "string",
      "userLabel": "string",
      "amountEuroCents": null
    }
  ],
  "fetchedAt": "string"
}
```

**400** – Invalid query parameters

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Failed to load recent activity

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/wallet/dashboards/recent-activity?limit=10" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/wallet/dashboards/total-balance`

Total wallet balance

Returns the sum of all wallet balances (in EUR cents) scoped to the caller's tenant/organization, plus the change versus the start of the requested date-range preset (default last_30_days).

Requires features: analytics.view, wallet.admin.list

**Tags:** Wallet Dashboards

**Requires authentication.**

**Features:** analytics.view, wallet.admin.list

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| preset | query | any | Optional |

### Responses

**200** – Total balance with monthly comparison

Content-Type: `application/json`

```json
{
  "value": 1,
  "comparison": null,
  "fetchedAt": "string"
}
```

**500** – Failed to load total balance

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/wallet/dashboards/total-balance" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/wallet/me/wallet/auto-refill`

Get auto-refill configuration

Returns the current user auto-refill configuration and monthly-spent counter derived from succeeded auto-refill payments since the start of the current UTC month. Canonical EUR fields are always present, including the picker bounds amountRangeEuroCents (the amount slider, €5–€500) and thresholdRangeEuroCents (the "when balance below" slider, €0–€200). Non-EUR users additionally get display-currency variants: displayAmount (the frozen charged amount), displayThreshold and displayMonthlyCap (EUR-canonical, shown in the display currency), displayMonthlySpent (converted live), and displayAmountRange / displayThresholdRange (the localized, hysteresis-dampened slider bounds — min rounded up, max rounded down). All display fields are omitted for EUR users and on any FX failure.

Requires features: wallet.balance.view

**Tags:** Wallet

**Requires authentication.**

**Features:** wallet.balance.view

### Responses

**200** – Auto-refill configuration

Content-Type: `application/json`

```json
{
  "enabled": true,
  "withdrawalConsent": true,
  "amountEuroCents": null,
  "thresholdEuroCents": null,
  "monthlyCapEuroCents": null,
  "monthlySpentEuroCents": 1,
  "pausedAt": null,
  "pausedReason": null,
  "amountRangeEuroCents": {
    "minEuroCents": 1,
    "maxEuroCents": 1
  },
  "thresholdRangeEuroCents": {
    "minEuroCents": 1,
    "maxEuroCents": 1
  }
}
```

**404** – Consumer profile not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/wallet/me/wallet/auto-refill" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## PUT `/wallet/me/wallet/auto-refill`

Update auto-refill configuration

Updates the current user auto-refill configuration. The amount, threshold and monthlyCap are supplied in the user display currency ({ amountInMinorUnits, currency }); the backend derives the canonical EUR value at set time and freezes the entered amount for verbatim re-display. The body currency MUST match the account display currency — a mismatch is rejected with 400 rather than silently reinterpreted. The derived EUR amount must fall within €5–€500 and the threshold within €0–€200, else the request is rejected with 400. The amount is charged in that display currency at each trigger with the EUR credited computed fresh (a saved top-up instruction). Setting enabled=true implicitly clears pause and requires withdrawalConsent=true (the user expressly agrees to wallet credits before the expiry of the withdrawal period and acknowledges losing their statutory right of withdrawal); the request is rejected with 400 otherwise. The consent is stored on the profile and cloned onto each auto-refill Payment (withdrawal_consent_source=auto_refill). Returns 422 when the chosen currency cannot be converted.

Requires features: wallet.balance.view

**Tags:** Wallet

**Requires authentication.**

**Features:** wallet.balance.view

### Request Body

Content-Type: `application/json`

```json
{
  "enabled": true,
  "amount": null,
  "threshold": null,
  "monthlyCap": null
}
```

### Responses

**200** – Auto-refill configuration updated

Content-Type: `application/json`

```json
{
  "enabled": true,
  "withdrawalConsent": true,
  "amountEuroCents": null,
  "thresholdEuroCents": null,
  "monthlyCapEuroCents": null,
  "monthlySpentEuroCents": 1,
  "pausedAt": null,
  "pausedReason": null,
  "amountRangeEuroCents": {
    "minEuroCents": 1,
    "maxEuroCents": 1
  },
  "thresholdRangeEuroCents": {
    "minEuroCents": 1,
    "maxEuroCents": 1
  }
}
```

**400** – Invalid payload

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Consumer profile not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**422** – Selected currency cannot be converted

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/wallet/me/wallet/auto-refill" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{
  \"enabled\": true,
  \"amount\": null,
  \"threshold\": null,
  \"monthlyCap\": null
}"
```

## GET `/wallet/me/wallets`

Get current user wallets

Returns standard, bonus, and combined virtual wallet with balances.

Requires features: wallet.balance.view

**Tags:** Wallet

**Requires authentication.**

**Features:** wallet.balance.view

### Responses

**200** – User wallets

Content-Type: `application/json`

```json
{
  "wallets": [
    {
      "id": null,
      "type": "standard",
      "balance": {
        "amountInMinorUnits": 1,
        "currency": "string"
      }
    }
  ]
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/wallet/me/wallets" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/wallet/me/wallets/transactions`

Get transaction history

Returns paginated transaction history across all wallets, sorted by date descending. Each item carries the canonical EUR `amount`; non-EUR users additionally get a frozen `displayAmount` (the value in their display currency at event time, read back verbatim).

Requires features: wallet.transactions.view

**Tags:** Wallet

**Requires authentication.**

**Features:** wallet.transactions.view

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| limit | query | any | Optional |
| offset | query | any | Optional |
| type | query | any | Optional |
| walletType | query | any | Optional |

### Responses

**200** – Transaction list

Content-Type: `application/json`

```json
{
  "transactions": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "walletId": "00000000-0000-4000-8000-000000000000",
      "walletType": "standard",
      "type": "credit",
      "amount": {
        "amountInMinorUnits": 1,
        "currency": "string"
      },
      "description": "string",
      "referenceType": "top_up",
      "referenceId": null,
      "countryCodes": null,
      "usageBytes": null,
      "invoiceId": null,
      "creditNoteId": null,
      "createdAt": "string"
    }
  ],
  "total": 1
}
```

**400** – Invalid query

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/wallet/me/wallets/transactions?limit=20&offset=0" \
  -H "Accept: application/json" \
  -H "authorization: Bearer <token>"
```

## GET `/workflows/definitions`

List workflow definitions

Get a list of workflow definitions with optional filters. Supports pagination and search.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| workflowId | query | any | Required |
| enabled | query | any | Optional |
| search | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – List of workflow definitions with pagination

Content-Type: `application/json`

```json
{
  "data": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "workflowId": "checkout-flow",
      "workflowName": "Checkout Flow",
      "description": "Complete checkout workflow for processing orders",
      "version": 1,
      "definition": {
        "steps": [
          {
            "stepId": "start",
            "stepName": "Start",
            "stepType": "START"
          },
          {
            "stepId": "validate-cart",
            "stepName": "Validate Cart",
            "stepType": "AUTOMATED"
          },
          {
            "stepId": "end",
            "stepName": "End",
            "stepType": "END"
          }
        ],
        "transitions": [
          {
            "transitionId": "start-to-validate",
            "fromStepId": "start",
            "toStepId": "validate-cart",
            "trigger": "auto"
          },
          {
            "transitionId": "validate-to-end",
            "fromStepId": "validate-cart",
            "toStepId": "end",
            "trigger": "auto"
          }
        ]
      },
      "enabled": true,
      "tenantId": "123e4567-e89b-12d3-a456-426614174001",
      "organizationId": "123e4567-e89b-12d3-a456-426614174002",
      "createdAt": "2025-12-08T10:00:00.000Z",
      "updatedAt": "2025-12-08T10:00:00.000Z"
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 50,
    "offset": 0,
    "hasMore": false
  }
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/workflows/definitions?workflowId=string&limit=50&offset=0" \
  -H "Accept: application/json"
```

## POST `/workflows/definitions`

Create workflow definition

Create a new workflow definition. The definition must include at least START and END steps with at least one transition connecting them.

**Tags:** Workflows

### Request Body

Content-Type: `application/json`

```json
{
  "workflowId": "checkout-flow",
  "workflowName": "Checkout Flow",
  "description": "Complete checkout workflow for processing orders",
  "version": 1,
  "definition": {
    "steps": [
      {
        "stepId": "start",
        "stepName": "Start",
        "stepType": "START"
      },
      {
        "stepId": "validate-cart",
        "stepName": "Validate Cart",
        "stepType": "AUTOMATED",
        "description": "Validate cart items and check inventory"
      },
      {
        "stepId": "payment",
        "stepName": "Process Payment",
        "stepType": "AUTOMATED",
        "description": "Charge payment method",
        "retryPolicy": {
          "maxAttempts": 3,
          "backoffMs": 1000
        }
      },
      {
        "stepId": "end",
        "stepName": "End",
        "stepType": "END"
      }
    ],
    "transitions": [
      {
        "transitionId": "start-to-validate",
        "fromStepId": "start",
        "toStepId": "validate-cart",
        "trigger": "auto"
      },
      {
        "transitionId": "validate-to-payment",
        "fromStepId": "validate-cart",
        "toStepId": "payment",
        "trigger": "auto"
      },
      {
        "transitionId": "payment-to-end",
        "fromStepId": "payment",
        "toStepId": "end",
        "trigger": "auto",
        "activities": [
          {
            "activityName": "Send Order Confirmation",
            "activityType": "SEND_EMAIL",
            "config": {
              "to": "{{context.customerEmail}}",
              "subject": "Order Confirmation #{{context.orderId}}",
              "template": "order_confirmation"
            }
          }
        ]
      }
    ]
  },
  "enabled": true
}
```

### Responses

**201** – Workflow definition created successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "workflowId": "checkout-flow",
    "workflowName": "Checkout Flow",
    "description": "Complete checkout workflow for processing orders",
    "version": 1,
    "definition": {
      "steps": [
        {
          "stepId": "start",
          "stepName": "Start",
          "stepType": "START"
        },
        {
          "stepId": "validate-cart",
          "stepName": "Validate Cart",
          "stepType": "AUTOMATED"
        },
        {
          "stepId": "payment",
          "stepName": "Process Payment",
          "stepType": "AUTOMATED"
        },
        {
          "stepId": "end",
          "stepName": "End",
          "stepType": "END"
        }
      ],
      "transitions": [
        {
          "transitionId": "start-to-validate",
          "fromStepId": "start",
          "toStepId": "validate-cart",
          "trigger": "auto"
        },
        {
          "transitionId": "validate-to-payment",
          "fromStepId": "validate-cart",
          "toStepId": "payment",
          "trigger": "auto"
        },
        {
          "transitionId": "payment-to-end",
          "fromStepId": "payment",
          "toStepId": "end",
          "trigger": "auto"
        }
      ]
    },
    "enabled": true,
    "tenantId": "123e4567-e89b-12d3-a456-426614174001",
    "organizationId": "123e4567-e89b-12d3-a456-426614174002",
    "createdAt": "2025-12-08T10:00:00.000Z",
    "updatedAt": "2025-12-08T10:00:00.000Z"
  },
  "message": "Workflow definition created successfully"
}
```

**400** – Validation error - invalid workflow structure

Content-Type: `application/json`

```json
{
  "error": "Validation failed",
  "details": [
    {
      "code": "invalid_type",
      "message": "Workflow must have at least START and END steps",
      "path": [
        "definition",
        "steps"
      ]
    }
  ]
}
```

**409** – Conflict - workflow with same ID and version already exists

Content-Type: `application/json`

```json
{
  "error": "Workflow definition with ID \"checkout-flow\" and version 1 already exists"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/workflows/definitions" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"workflowId\": \"string\",
  \"workflowName\": \"string\",
  \"description\": null,
  \"version\": 1,
  \"definition\": {
    \"steps\": [
      {
        \"stepId\": \"string\",
        \"stepName\": \"string\",
        \"stepType\": \"START\"
      }
    ],
    \"transitions\": [
      {
        \"transitionId\": \"string\",
        \"fromStepId\": \"string\",
        \"toStepId\": \"string\",
        \"trigger\": \"auto\",
        \"continueOnActivityFailure\": false,
        \"priority\": 0
      }
    ]
  },
  \"metadata\": null,
  \"enabled\": true
}"
```

## DELETE `/workflows/definitions/{id}`

Delete workflow definition

Soft delete a workflow definition. Cannot be deleted if there are active workflow instances (RUNNING or WAITING status) using this definition.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Workflow definition deleted successfully

Content-Type: `application/json`

```json
{
  "message": "Workflow definition deleted successfully"
}
```

**404** – Workflow definition not found

Content-Type: `application/json`

```json
{
  "error": "Workflow definition not found"
}
```

**409** – Cannot delete - active workflow instances exist

Content-Type: `application/json`

```json
{
  "error": "Cannot delete workflow definition with 3 active instance(s)"
}
```

### Example

```bash
curl -X DELETE "https://admin-dev.getcovo.com/api/workflows/definitions/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json"
```

## GET `/workflows/definitions/{id}`

Get workflow definition

Get a single workflow definition by ID. Returns the complete workflow structure including steps and transitions (with embedded activities).

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required. UUID for DB definitions, or "code:<workflowId>" for code-based definitions |

### Responses

**200** – Workflow definition found

Content-Type: `application/json`

```json
{
  "data": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "workflowId": "checkout-flow",
    "workflowName": "Checkout Flow",
    "description": "Complete checkout workflow for processing orders",
    "version": 1,
    "definition": {
      "steps": [
        {
          "stepId": "start",
          "stepName": "Start",
          "stepType": "START"
        },
        {
          "stepId": "validate-cart",
          "stepName": "Validate Cart",
          "stepType": "AUTOMATED",
          "description": "Validate cart items and check inventory"
        },
        {
          "stepId": "payment",
          "stepName": "Process Payment",
          "stepType": "AUTOMATED",
          "description": "Charge payment method",
          "retryPolicy": {
            "maxAttempts": 3,
            "backoffMs": 1000
          }
        },
        {
          "stepId": "end",
          "stepName": "End",
          "stepType": "END"
        }
      ],
      "transitions": [
        {
          "transitionId": "start-to-validate",
          "fromStepId": "start",
          "toStepId": "validate-cart",
          "trigger": "auto"
        },
        {
          "transitionId": "validate-to-payment",
          "fromStepId": "validate-cart",
          "toStepId": "payment",
          "trigger": "auto"
        },
        {
          "transitionId": "payment-to-end",
          "fromStepId": "payment",
          "toStepId": "end",
          "trigger": "auto",
          "activities": [
            {
              "activityName": "Send Order Confirmation",
              "activityType": "SEND_EMAIL",
              "config": {
                "to": "{{context.customerEmail}}",
                "subject": "Order Confirmation #{{context.orderId}}",
                "template": "order_confirmation"
              }
            }
          ]
        }
      ]
    },
    "enabled": true,
    "tenantId": "123e4567-e89b-12d3-a456-426614174001",
    "organizationId": "123e4567-e89b-12d3-a456-426614174002",
    "createdAt": "2025-12-08T10:00:00.000Z",
    "updatedAt": "2025-12-08T10:00:00.000Z"
  }
}
```

**404** – Workflow definition not found

Content-Type: `application/json`

```json
{
  "error": "Workflow definition not found"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/workflows/definitions/string" \
  -H "Accept: application/json"
```

## PUT `/workflows/definitions/{id}`

Update workflow definition

Update an existing workflow definition. Supports partial updates - only provided fields will be updated.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "definition": {
    "steps": [
      {
        "stepId": "start",
        "stepName": "Start",
        "stepType": "START"
      },
      {
        "stepId": "validate-cart",
        "stepName": "Validate Cart",
        "stepType": "AUTOMATED"
      },
      {
        "stepId": "payment",
        "stepName": "Process Payment",
        "stepType": "AUTOMATED"
      },
      {
        "stepId": "confirmation",
        "stepName": "Order Confirmation",
        "stepType": "AUTOMATED"
      },
      {
        "stepId": "end",
        "stepName": "End",
        "stepType": "END"
      }
    ],
    "transitions": [
      {
        "transitionId": "start-to-validate",
        "fromStepId": "start",
        "toStepId": "validate-cart",
        "trigger": "auto"
      },
      {
        "transitionId": "validate-to-payment",
        "fromStepId": "validate-cart",
        "toStepId": "payment",
        "trigger": "auto"
      },
      {
        "transitionId": "payment-to-confirmation",
        "fromStepId": "payment",
        "toStepId": "confirmation",
        "trigger": "auto"
      },
      {
        "transitionId": "confirmation-to-end",
        "fromStepId": "confirmation",
        "toStepId": "end",
        "trigger": "auto"
      }
    ]
  },
  "enabled": true
}
```

### Responses

**200** – Workflow definition updated successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "workflowId": "checkout-flow",
    "workflowName": "Checkout Flow",
    "description": "Complete checkout workflow for processing orders",
    "version": 1,
    "definition": {
      "steps": [
        {
          "stepId": "start",
          "stepName": "Start",
          "stepType": "START"
        },
        {
          "stepId": "validate-cart",
          "stepName": "Validate Cart",
          "stepType": "AUTOMATED"
        },
        {
          "stepId": "payment",
          "stepName": "Process Payment",
          "stepType": "AUTOMATED"
        },
        {
          "stepId": "confirmation",
          "stepName": "Order Confirmation",
          "stepType": "AUTOMATED"
        },
        {
          "stepId": "end",
          "stepName": "End",
          "stepType": "END"
        }
      ],
      "transitions": [
        {
          "transitionId": "start-to-validate",
          "fromStepId": "start",
          "toStepId": "validate-cart",
          "trigger": "auto"
        },
        {
          "transitionId": "validate-to-payment",
          "fromStepId": "validate-cart",
          "toStepId": "payment",
          "trigger": "auto"
        },
        {
          "transitionId": "payment-to-confirmation",
          "fromStepId": "payment",
          "toStepId": "confirmation",
          "trigger": "auto"
        },
        {
          "transitionId": "confirmation-to-end",
          "fromStepId": "confirmation",
          "toStepId": "end",
          "trigger": "auto"
        }
      ]
    },
    "enabled": true,
    "tenantId": "123e4567-e89b-12d3-a456-426614174001",
    "organizationId": "123e4567-e89b-12d3-a456-426614174002",
    "createdAt": "2025-12-08T10:00:00.000Z",
    "updatedAt": "2025-12-08T11:30:00.000Z"
  },
  "message": "Workflow definition updated successfully"
}
```

**400** – Validation error

Content-Type: `application/json`

```json
{
  "error": "Validation failed",
  "details": [
    {
      "code": "invalid_type",
      "message": "Expected object, received string",
      "path": [
        "definition"
      ]
    }
  ]
}
```

**404** – Workflow definition not found

Content-Type: `application/json`

```json
{
  "error": "Workflow definition not found"
}
```

### Example

```bash
curl -X PUT "https://admin-dev.getcovo.com/api/workflows/definitions/00000000-0000-4000-8000-000000000000" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"description\": null,
  \"metadata\": null,
  \"effectiveFrom\": null,
  \"effectiveTo\": null
}"
```

## POST `/workflows/definitions/{id}/customize`

Customize code-based workflow definition

Creates a DB override for a code-based workflow definition, seeded from the current code registry values. The id param must be of the form "code:<workflowId>".

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required. Must be of the form "code:<workflowId>" |

### Responses

**200** – Workflow definition customized successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "workflowId": "workflows.simple-approval",
    "workflowName": "Simple Approval Workflow",
    "source": "code_override"
  },
  "message": "Workflow definition customized successfully"
}
```

**400** – Not a code-based id

Content-Type: `application/json`

```json
{
  "error": "Customize is only supported for code-based workflow definitions"
}
```

**404** – Code workflow not found

Content-Type: `application/json`

```json
{
  "error": "Workflow definition not found"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/workflows/definitions/string/customize" \
  -H "Accept: application/json"
```

## POST `/workflows/definitions/{id}/reset-to-code`

Reset workflow definition to code version

Deletes the DB override for a code-based workflow definition, reverting it to the original code registry version. Cannot be reset if there are active instances.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Workflow definition reset to code version

Content-Type: `application/json`

```json
{
  "data": {
    "id": "code:checkout-flow",
    "workflowId": "checkout-flow",
    "workflowName": "Checkout Flow",
    "description": "Code-defined checkout workflow",
    "version": 1,
    "source": "code",
    "isCodeBased": true
  },
  "message": "Workflow definition reset to code version"
}
```

**400** – Definition is not a code-based override

Content-Type: `application/json`

```json
{
  "error": "This workflow definition is not a code-based override and cannot be reset"
}
```

**404** – Workflow definition not found

Content-Type: `application/json`

```json
{
  "error": "Workflow definition not found"
}
```

**409** – Cannot reset - active workflow instances exist

Content-Type: `application/json`

```json
{
  "error": "Cannot reset workflow definition with 3 active instance(s)"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/workflows/definitions/00000000-0000-4000-8000-000000000000/reset-to-code" \
  -H "Accept: application/json"
```

## GET `/workflows/events`

List all workflow events

Get a paginated list of all workflow events with filtering options

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| page | query | any | Optional |
| pageSize | query | any | Optional |
| eventType | query | any | Optional |
| workflowInstanceId | query | any | Optional |
| userId | query | any | Optional |
| occurredAtFrom | query | any | Optional |
| occurredAtTo | query | any | Optional |
| sortField | query | any | Optional |
| sortDir | query | any | Optional |

### Responses

**200** – List of workflow events

Content-Type: `application/json`

```json
{
  "items": [
    {
      "id": "string",
      "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
      "stepInstanceId": null,
      "eventType": "string",
      "occurredAt": "string",
      "userId": null,
      "workflowInstance": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 1,
  "totalPages": 1
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/workflows/events?page=1&pageSize=50&sortField=occurredAt&sortDir=desc" \
  -H "Accept: application/json"
```

## GET `/workflows/events/{id}`

Get workflow event by ID

Get detailed information about a specific workflow event

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Workflow event details

Content-Type: `application/json`

```json
{
  "id": "string",
  "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
  "stepInstanceId": null,
  "eventType": "string",
  "occurredAt": "string",
  "userId": null,
  "tenantId": "00000000-0000-4000-8000-000000000000",
  "organizationId": "00000000-0000-4000-8000-000000000000",
  "workflowInstance": null
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow event not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/workflows/events/:id" \
  -H "Accept: application/json"
```

## GET `/workflows/instances`

List workflow instances

Get a list of workflow instances with optional filters. Supports pagination and filtering by status, workflowId, correlationKey, etc.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| workflowId | query | any | Optional |
| status | query | any | Optional |
| correlationKey | query | any | Optional |
| entityType | query | any | Optional |
| entityId | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – List of workflow instances

Content-Type: `application/json`

```json
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "definitionId": "00000000-0000-4000-8000-000000000000",
      "workflowId": "string",
      "version": 1,
      "status": "RUNNING",
      "currentStepId": "string",
      "correlationKey": null,
      "metadata": null,
      "startedAt": "string",
      "completedAt": null,
      "pausedAt": null,
      "cancelledAt": null,
      "errorMessage": null,
      "errorDetails": null,
      "pendingTransition": null,
      "retryCount": 1,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdAt": "string",
      "updatedAt": "string",
      "deletedAt": null
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 1,
    "offset": 1,
    "hasMore": true
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/workflows/instances?limit=50&offset=0" \
  -H "Accept: application/json"
```

## POST `/workflows/instances`

Start workflow instance

Start a new workflow instance from a workflow definition. The workflow will execute immediately.

**Tags:** Workflows

### Request Body

Content-Type: `application/json`

```json
{
  "workflowId": "string"
}
```

### Responses

**201** – Workflow started successfully

Content-Type: `application/json`

```json
{
  "data": {
    "instance": {
      "id": "00000000-0000-4000-8000-000000000000",
      "definitionId": "00000000-0000-4000-8000-000000000000",
      "workflowId": "string",
      "version": 1,
      "status": "RUNNING",
      "currentStepId": "string",
      "correlationKey": null,
      "metadata": null,
      "startedAt": "string",
      "completedAt": null,
      "pausedAt": null,
      "cancelledAt": null,
      "errorMessage": null,
      "errorDetails": null,
      "pendingTransition": null,
      "retryCount": 1,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdAt": "string",
      "updatedAt": "string",
      "deletedAt": null
    },
    "execution": {
      "status": "RUNNING",
      "currentStep": "string",
      "message": "string"
    }
  },
  "message": "string"
}
```

**400** – Bad request - Validation failed or definition disabled/invalid

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow definition not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/workflows/instances" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"workflowId\": \"string\"
}"
```

## GET `/workflows/instances/{id}`

Get workflow instance

Get detailed information about a specific workflow instance including current state, context, and execution status.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Workflow instance details

Content-Type: `application/json`

```json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000000",
    "definitionId": "00000000-0000-4000-8000-000000000000",
    "workflowId": "string",
    "version": 1,
    "status": "RUNNING",
    "currentStepId": "string",
    "correlationKey": null,
    "metadata": null,
    "startedAt": "string",
    "completedAt": null,
    "pausedAt": null,
    "cancelledAt": null,
    "errorMessage": null,
    "errorDetails": null,
    "pendingTransition": null,
    "retryCount": 1,
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string",
    "deletedAt": null
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/workflows/instances/:id" \
  -H "Accept: application/json"
```

## POST `/workflows/instances/{id}/advance`

Manually advance workflow to next step

Manually advance a workflow instance to the next step. Useful for manual progression, step-by-step testing, user-triggered transitions, and approval flows. Validates transitions and auto-progresses if possible.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{}
```

### Responses

**200** – Workflow advanced successfully

Content-Type: `application/json`

```json
{
  "data": {
    "instance": {
      "id": "00000000-0000-4000-8000-000000000000",
      "status": "string",
      "currentStepId": null,
      "previousStepId": null,
      "transitionFired": null
    }
  },
  "message": "string"
}
```

**400** – Invalid request, no valid transitions, or workflow already completed/cancelled/failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/workflows/instances/:id/advance" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{}"
```

## POST `/workflows/instances/{id}/cancel`

Cancel workflow instance

Cancel a running or paused workflow instance. The workflow will be marked as CANCELLED and will not execute further.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Workflow cancelled successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000000",
    "definitionId": "00000000-0000-4000-8000-000000000000",
    "workflowId": "string",
    "version": 1,
    "status": "RUNNING",
    "currentStepId": "string",
    "correlationKey": null,
    "metadata": null,
    "startedAt": "string",
    "completedAt": null,
    "pausedAt": null,
    "cancelledAt": null,
    "errorMessage": null,
    "errorDetails": null,
    "pendingTransition": null,
    "retryCount": 1,
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string",
    "deletedAt": null
  },
  "message": "string"
}
```

**400** – Bad request - Workflow cannot be cancelled in current status

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/workflows/instances/:id/cancel" \
  -H "Accept: application/json"
```

## GET `/workflows/instances/{id}/events`

Get workflow instance events

Get a chronological list of events for a workflow instance. Events track all state changes, transitions, and activities.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |
| eventType | query | any | Optional |
| limit | query | any | Optional |
| offset | query | any | Optional |

### Responses

**200** – List of workflow events

Content-Type: `application/json`

```json
{
  "data": [
    {
      "id": "string",
      "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
      "stepInstanceId": null,
      "eventType": "string",
      "occurredAt": "string",
      "userId": null,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000"
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 1,
    "offset": 1,
    "hasMore": true
  }
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/workflows/instances/:id/events?limit=100&offset=0" \
  -H "Accept: application/json"
```

## POST `/workflows/instances/{id}/retry`

Retry failed workflow instance

Retry a failed workflow instance from its current step. The workflow will be reset to RUNNING status and execution will continue.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Workflow retry initiated successfully

Content-Type: `application/json`

```json
{
  "data": {
    "instance": {
      "id": "00000000-0000-4000-8000-000000000000",
      "definitionId": "00000000-0000-4000-8000-000000000000",
      "workflowId": "string",
      "version": 1,
      "status": "RUNNING",
      "currentStepId": "string",
      "correlationKey": null,
      "metadata": null,
      "startedAt": "string",
      "completedAt": null,
      "pausedAt": null,
      "cancelledAt": null,
      "errorMessage": null,
      "errorDetails": null,
      "pendingTransition": null,
      "retryCount": 1,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdAt": "string",
      "updatedAt": "string",
      "deletedAt": null
    },
    "execution": {
      "status": "RUNNING",
      "currentStep": "string",
      "events": [
        {
          "eventType": "string",
          "occurredAt": "string"
        }
      ],
      "executionTime": 1
    }
  },
  "message": "string"
}
```

**400** – Bad request - Workflow cannot be retried in current status or execution error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Forbidden - Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Workflow instance not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/workflows/instances/:id/retry" \
  -H "Accept: application/json"
```

## POST `/workflows/instances/{id}/signal`

Send signal to specific workflow

Sends a signal to a specific workflow instance waiting for a signal. The workflow must be in PAUSED status and waiting for the specified signal.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "signalName": "string"
}
```

### Responses

**200** – Signal sent successfully

Content-Type: `application/json`

```json
{
  "success": true,
  "message": "string"
}
```

**400** – Invalid request body or signal name mismatch

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Instance or definition not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Workflow not paused or not waiting for signal

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error or transition failed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/workflows/instances/:id/signal" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"signalName\": \"string\"
}"
```

## POST `/workflows/instances/validate-start`

Validate if workflow can be started

Evaluates pre-conditions defined on the START step and returns validation errors with localized messages if any fail. Returns canStart: true/false with details.

**Tags:** Workflows

### Request Body

Content-Type: `application/json`

```json
{
  "workflowId": "string"
}
```

### Responses

**200** – Validation result (canStart, errors, validatedRules)

Content-Type: `application/json`

```json
{
  "canStart": true,
  "workflowId": "string"
}
```

**400** – Invalid request body or missing context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/workflows/instances/validate-start" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"workflowId\": \"string\"
}"
```

## POST `/workflows/signals`

Send signal to workflows by correlation key

Sends a signal to all workflow instances waiting for the specified signal that match the correlation key. Returns the count of workflows that received the signal.

**Tags:** Workflows

### Request Body

Content-Type: `application/json`

```json
{
  "correlationKey": "string",
  "signalName": "string"
}
```

### Responses

**200** – Signal sent to matching workflows

Content-Type: `application/json`

```json
{
  "success": true,
  "message": "string",
  "count": 1
}
```

**400** – Missing tenant or organization context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**403** – Insufficient permissions

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/workflows/signals" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"correlationKey\": \"string\",
  \"signalName\": \"string\"
}"
```

## GET `/workflows/tasks`

List user tasks

Returns paginated list of user tasks with optional filtering by status, assignee, workflow instance, overdue, and myTasks flags.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| status | query | any | Optional. Filter by status (comma-separated for multiple: PENDING,IN_PROGRESS,COMPLETED,CANCELLED,ESCALATED) |
| assignedTo | query | any | Optional. Filter by assigned user ID |
| workflowInstanceId | query | any | Optional. Filter by workflow instance ID |
| overdue | query | any | Optional. Filter overdue tasks (true/false) |
| myTasks | query | any | Optional. Show only tasks assigned to or claimable by current user |
| limit | query | any | Optional. Number of results (max 100) |
| offset | query | any | Optional. Pagination offset |

### Responses

**200** – User tasks list with pagination

Content-Type: `application/json`

```json
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
      "stepInstanceId": "00000000-0000-4000-8000-000000000000",
      "taskName": "string",
      "description": null,
      "status": "PENDING",
      "formSchema": null,
      "formData": null,
      "assignedTo": null,
      "assignedToRoles": null,
      "claimedBy": null,
      "claimedAt": null,
      "dueDate": null,
      "escalatedAt": null,
      "escalatedTo": null,
      "completedBy": null,
      "completedAt": null,
      "tenantId": "00000000-0000-4000-8000-000000000000",
      "organizationId": "00000000-0000-4000-8000-000000000000",
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 1,
    "offset": 1,
    "hasMore": true
  }
}
```

**400** – Invalid query parameters or missing tenant context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/workflows/tasks?limit=50&offset=0" \
  -H "Accept: application/json"
```

## GET `/workflows/tasks/{id}`

Get task details

Returns complete details of a user task by ID.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – User task details

Content-Type: `application/json`

```json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000000",
    "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
    "stepInstanceId": "00000000-0000-4000-8000-000000000000",
    "taskName": "string",
    "description": null,
    "status": "PENDING",
    "formSchema": null,
    "formData": null,
    "assignedTo": null,
    "assignedToRoles": null,
    "claimedBy": null,
    "claimedAt": null,
    "dueDate": null,
    "escalatedAt": null,
    "escalatedTo": null,
    "completedBy": null,
    "completedAt": null,
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string"
  }
}
```

**400** – Missing tenant or organization context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Task not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X GET "https://admin-dev.getcovo.com/api/workflows/tasks/:id" \
  -H "Accept: application/json"
```

## POST `/workflows/tasks/{id}/claim`

Claim a task from role queue

Allows a user to claim a task assigned to their role(s). Once claimed, the task moves to IN_PROGRESS status and is assigned to the claiming user.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Responses

**200** – Task claimed successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000000",
    "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
    "stepInstanceId": "00000000-0000-4000-8000-000000000000",
    "taskName": "string",
    "description": null,
    "status": "PENDING",
    "formSchema": null,
    "formData": null,
    "assignedTo": null,
    "assignedToRoles": null,
    "claimedBy": null,
    "claimedAt": null,
    "dueDate": null,
    "escalatedAt": null,
    "escalatedTo": null,
    "completedBy": null,
    "completedAt": null,
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string"
  },
  "message": "string"
}
```

**400** – Missing tenant or organization context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Task not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Task already claimed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/workflows/tasks/:id/claim" \
  -H "Accept: application/json"
```

## POST `/workflows/tasks/{id}/complete`

Complete a task with form data

Validates form data against task schema, updates task with completion data, merges form data into workflow context, and resumes workflow execution.

**Tags:** Workflows

### Parameters
| Name | Location | Type | Description |
| --- | --- | --- | --- |
| id | path | any | Required |

### Request Body

Content-Type: `application/json`

```json
{
  "formData": {}
}
```

### Responses

**200** – Task completed successfully

Content-Type: `application/json`

```json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000000",
    "workflowInstanceId": "00000000-0000-4000-8000-000000000000",
    "stepInstanceId": "00000000-0000-4000-8000-000000000000",
    "taskName": "string",
    "description": null,
    "status": "PENDING",
    "formSchema": null,
    "formData": null,
    "assignedTo": null,
    "assignedToRoles": null,
    "claimedBy": null,
    "claimedAt": null,
    "dueDate": null,
    "escalatedAt": null,
    "escalatedTo": null,
    "completedBy": null,
    "completedAt": null,
    "tenantId": "00000000-0000-4000-8000-000000000000",
    "organizationId": "00000000-0000-4000-8000-000000000000",
    "createdAt": "string",
    "updatedAt": "string"
  },
  "message": "string"
}
```

**400** – Invalid request body, validation failed, or missing context

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**401** – Unauthorized

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**404** – Task not found

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**409** – Task already completed

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

**500** – Internal server error

Content-Type: `application/json`

```json
{
  "error": "string"
}
```

### Example

```bash
curl -X POST "https://admin-dev.getcovo.com/api/workflows/tasks/:id/complete" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
  \"formData\": {}
}"
```