> ## Documentation Index
> Fetch the complete documentation index at: https://docs.glider.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# Admin Notifications

> Internal-only notification campaign endpoints for Retool and ops tooling

These endpoints let internal tools request Novu workflow sends without owning
audience logic. Retool passes a `workflowId`, a bounded JSON `payload`, and a
target; `platform-api` resolves canonical Glider user IDs and reconciles Novu
topics before delivery. Manual Novu-managed topics are allowlisted by the API
but their membership is maintained outside `platform-api`.

## Auth

All routes are protected by a scoped notifications admin token:

* `Authorization: Bearer <RETOOL_NOTIFICATIONS_ADMIN_TOKEN>`
* `Content-Type: application/json` for `POST` routes

`ADMIN_AUTH_SECRET_TOKEN` is also accepted for internal engineer/admin usage,
but Retool should use the scoped notifications token.

## List Topics

`GET /v1/admin/notifications/topics`

Returns the approved system topic catalog for Retool dropdowns:

```json theme={null}
{
  "topics": [
    {
      "key": "glider:users:depositors:funded",
      "label": "Current funded depositors",
      "description": "Users who have deposited and currently have a positive portfolio position.",
      "exclusive": true
    },
    {
      "key": "custom:users:power-users",
      "label": "Power users",
      "description": "Manual Novu-managed list of power users. Membership is maintained outside the API.",
      "exclusive": false
    }
  ]
}
```

## Send Notification

`POST /v1/admin/notifications/send`

```json theme={null}
{
  "workflowId": "platform_announcement",
  "payload": {
    "title": "New feature",
    "body": "Try it today"
  },
  "idempotencyKey": "campaign-2026-04-25",
  "target": {
    "type": "topic",
    "topicKey": "glider:users:depositors:funded"
  }
}
```

Targets:

* `{ "type": "userIds", "userIds": ["4ab64569-ce77-4337-ba5f-4b0224b32746"] }`
* `{ "type": "walletAddresses", "walletAddresses": ["0xe1cba71f8c268e866c1070c233113a0bdf2b2869"] }`
* `{ "type": "topic", "topicKey": "glider:users:depositors:funded" }`
* `{ "type": "topic", "topicKey": "custom:users:power-users" }`

`userIds` entries must be canonical Glider user IDs (UUIDs); any other shape
is rejected at request validation with a 400. Well-formed IDs that are
unknown or belong to deleted users fail the whole send with `USER_NOT_FOUND`.

`walletAddresses` sends accept EVM and Solana wallet addresses in any mix
(EVM addresses are case-insensitive; Solana addresses are case-sensitive) and
resolve each to its canonical Glider user before triggering the workflow.
Unlike `userIds`, unresolvable addresses do not fail the send: invalid,
unknown, or deleted-user addresses are skipped and echoed back in
`target.skippedAddresses`, alongside `requestedAddressCount` and
`resolvedSubscriberCount`. Two exceptions are enforced by request validation
instead: blank entries and entries longer than 200 characters reject the
whole request with a 400. `requestedAddressCount` counts unique addresses
after normalization, so the same EVM wallet submitted in multiple casings
counts once, and every requested address is accounted for as either backing a
resolved subscriber or listed in `skippedAddresses`. Multiple addresses owned
by the same user dedupe to a single delivery. If no address resolves, nothing
is triggered and the response reports `triggered: false`.

For DB-resolved `glider:users:*` topic sends, `platform-api` resolves the
current DB audience, ensures those canonical user IDs exist as Novu subscribers,
updates topic membership, and only then triggers the Novu workflow to the topic.

For manual `custom:*` topic sends, `platform-api` skips membership reconciliation
and only triggers the Novu workflow to the existing topic subscribers.

## Reconcile Topic

`POST /v1/admin/notifications/topics/:topicKey/reconcile`

```json theme={null}
{
  "dryRun": true
}
```

Rebuilds or previews a system topic from Glider DB. `dryRun: true` resolves and
diffs membership without mutating Novu subscriptions or triggering a workflow.
Manual topics are accepted by this endpoint but do not reconcile membership.
