# Notifications API

> List the session user's notification inbox and mark rows read.

Probara exposes two endpoints for the session user's notification inbox: a
cursor-paginated list and a mark-read action. Both require an authenticated
session — sign in in the browser and send the session cookie plus
`X-Organization-Id` when calling from custom scripts. Both are scoped to the
active organization; there is no bearer-token (API token) surface for
notifications.

## List notifications

`GET /api/v1/notifications`

Returns the session user's inbox for the active organization, newest first.

### Query parameters

| Parameter    | Type    | Notes                                                                                                                |
| ------------ | ------- | -------------------------------------------------------------------------------------------------------------------- |
| `cursor`     | ULID    | When set, returns rows after this cursor (older page)                                                                |
| `limit`      | integer | Page size. Default `20`. `0` is a valid explicit value. Values above `100` are rejected with `422 validation_failed` |
| `unreadOnly` | boolean | `true` returns only unread rows                                                                                      |

### Response shape

```json
{
  "items": [
    {
      "ulid": "01J…",
      "kind": "defect.assigned",
      "entityType": "defect",
      "entityUlid": "01J…",
      "actorLabel": "María López",
      "actorAvatarKey": "avatars/users/01J….webp",
      "actorAvatarVersion": 2,
      "eventCount": 1,
      "createdAt": 1700000000000,
      "readAt": null,
      "payload": { "entity": { "title": "Login regression", "url": "/x" } }
    }
  ],
  "nextCursor": "01J…",
  "unreadCount": 4
}
```

`unreadCount` reflects the same authorization and read-state filters as
`items`, so the two can never disagree — it is not a separate query.

### Kinds

`kind` is one of the notification catalog rows; `entityType` and `entityUlid` name what the
notification is about, and `payload.entity.url` links to it in the app.

| `kind`                                                                                                                                                     | `entityType`       |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| `test_case.comment_created`, `test_case.comment_mention`                                                                                                   | `test_case`        |
| `test_plan.case_assigned`                                                                                                                                  | `test_plan`        |
| `test_run.created_as_default_assignee`, `test_run.case_assigned`, `test_run.closed_or_aborted`                                                             | `test_run`         |
| `defect.assigned`, `defect.comment_created`, `defect.comment_mention`, `defect.status_changed`                                                             | `defect`           |
| `test_case_review.assigned`, `test_case_review.decided`, `test_case_review.edited`, `test_case_review.comment_created`, `test_case_review.comment_mention` | `test_case_review` |

A `test_case_review.decided` row carries the decision in `payload.context.status`: `approved`,
`changes_requested`, `declined` or `merged`. A review notification's `payload.entity.displayId` is
the request's `RV-` ID.

A bulk review action (sending several cases to review, or one decision on several requests) sends
one `test_case_review.assigned` or `test_case_review.decided` row per recipient with `entityType`
`test_case_review_batch`: `eventCount` is how many of the batch's requests concern the recipient,
`payload.entity.title` is the project's name and `payload.entity.url` opens the Review list.

### Example

```bash
curl -sS \
  -H "Cookie: $PROBARA_SESSION_COOKIE" \
  -H "X-Organization-Id: $ORG_ULID" \
  "https://app.probara.net/api/v1/notifications?limit=20&unreadOnly=true"
```

## Mark notifications read

`POST /api/v1/notifications/read`

### Request body

| Field   | Required | Notes                                                                                                                                                                 |
| ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ulids` | no       | Array of notification ULIDs to mark read, scoped to the caller's own rows regardless of value. Omitted or empty marks every visible row read. **At most 200 entries** |

### Request limits

`ulids` caps at **200 entries** — an abuse ceiling, not a product ceiling.
Submitting more than 200 returns `422 validation_failed` naming the `ulids`
field; no row changes read state and the unread count is unchanged.

Omitting `ulids` entirely is unaffected by this cap: it keeps its shipped
mark-all-visible-rows meaning.

### Response

`204 No Content` on success. Marking an already-read row (or replaying the
same request) is a no-op.

### Example — mark all visible rows read

```bash
curl -sS -X POST \
  -H "Cookie: $PROBARA_SESSION_COOKIE" \
  -H "X-Organization-Id: $ORG_ULID" \
  -H "Content-Type: application/json" \
  --data-raw '{}' \
  "https://app.probara.net/api/v1/notifications/read"
```

### Example — mark specific rows read

```bash
curl -sS -X POST \
  -H "Cookie: $PROBARA_SESSION_COOKIE" \
  -H "X-Organization-Id: $ORG_ULID" \
  -H "Content-Type: application/json" \
  -d '{"ulids": ["01J...A", "01J...B"]}' \
  "https://app.probara.net/api/v1/notifications/read"
```

## Related

- [API reference overview](https://docs.probara.net/en/developers/api-reference/) — pagination, errors, authentication
- [Interactive v1 reference](https://docs.probara.net/en/developers/api-reference/v1/) — full schemas
- [Audit events API](https://docs.probara.net/en/developers/api/audit-events/) — the organization-wide activity feed
