CorebanqCorebanq Developer Docs
Notificationsv1

List notifications

The shared list envelope, scoped through GetAllTotalInput with RbacRecordType = the notification record type. THAT RBAC SCOPE IS THE ONLY ONE — THESE ARE NOT "THE CALLER'S OWN ROWS". Neither the handler nor GetTotalNotifications/FetchNotifications adds a user_id predicate, so what a caller sees is decided entirely by GetPermittedRecords: a caller holding read_all on the notification record type gets EVERY user's notifications, and any other caller gets the rows they hold a read grant on, whoever they belong to. Pass search.user_id to narrow the list to one user. Filterable fields are id, customer_id, user_id, event, title, content, rec_id, rec_type, created_at, sent_at, read and read_at. title AND content DO NOT HOLD WHAT THE USER SEES. Both filter against the stored columns, and CreateUserNotifications never assigns Title at all, so user_notifications.title is empty on every row and search.title can never match anything. content is only the caller-supplied pre-rendered text, so it is empty for key-based notifications. The keys live in title_key and content_key, which are hidden from JSON and are not filterable. The title and content in the RESPONSE are rendered per request, after the filter has already run, and on this route they come from the event push template with the row metadata whenever one resolves — replacing the stored content. See the UserNotification schema: the three routes that return it resolve these two fields three different ways. ANY OTHER search.<field> IS REFUSED, AND REFUSED AS A 500: query.validateFieldName builds query_m.invalid_search_field without a status, and an empty Code sends HandleAppErrorWithCode to InternalServerError500. It is a caller error wearing a server error's status. sort is validated too, but on the fetch pass only — applyParams skips sorting when counting. So an unknown sort field is 400 query_m.invalid_sort_field when the filter matched at least one row, and is never noticed at all when the count came back zero or limit=-1 skipped the fetch: those answer 200 with an empty page.

GET
/v1/notifications

Authorization

BearerAuth
AuthorizationBearer <token>

In: header

Query Parameters

customer_id?string

NOT A SCOPE. GetNotificationsSrv uses it for one thing only — picking the language for the rendered title and content, through GetUserPreferredLanguage. It filters nothing; use search.customer_id for that. Omitted, it stays the nil uuid and is passed through as such. A value that does not parse is refused 400 — but with common.invalid_input, not the notifications_m.invalid_id the service builds: the handler discards that AppError and calls BadRequest400 with no argument.

limit?integer

Default 10, over 100 clamps to 100, zero or negative becomes 10, -1 is the skip-data-fetching sentinel. Non-integer is 400.

offset?integer

Default 0; negative is clamped to 0.

sort?string
stack?string

GROUPS THE PAGE AND CHANGES THE SHAPE OF data FROM AN ARRAY TO AN OBJECT keyed by the formatted value of the named field, with keys listing those group keys in order. Accepts the same field names as the filter list, optionally prefixed with - for descending and suffixed with [format]. A field outside the list is 400 query_m.invalid_field and an unterminated bracket is 400 query_m.invalid_format — but, like sort, only once rows have been fetched: a zero-row result returns the empty stacked shape without parsing the value at all.

distinct?string

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/notifications"
{
  "data": [
    {
      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
      "title": "string",
      "content": "string",
      "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5",
      "customer_id": "160c0c4b-9966-4dc1-a916-8407eb10d74e",
      "event": "string",
      "rec_id": "0ba687c4-349e-490d-969f-fd374e232d42",
      "rec_type": "string",
      "created_at": "2019-08-24T14:15:22Z",
      "sent_at": "2019-08-24T14:15:22Z",
      "read": true,
      "read_at": "2019-08-24T14:15:22Z",
      "system_sse_delivered_at": "2019-08-24T14:15:22Z",
      "channels_attempted": [
        "string"
      ],
      "channels_sent": [
        "string"
      ],
      "channels_delivered": [
        "string"
      ],
      "channels_failed": [
        "string"
      ],
      "link_url": "string",
      "link_text": "string",
      "created_by": "ee824cad-d7a6-4f48-87dc-e8461a9201c4",
      "modified_at": "2019-08-24T14:15:22Z",
      "modified_by": "e8d4374d-93a1-4e98-a6c6-fdcf00c5059f",
      "active": true,
      "metadata": {}
    }
  ],
  "total": 0,
  "total_unfiltered": 0,
  "has_more": true,
  "keys": [
    "string"
  ],
  "metadata": {}
}
{
  "status": 400,
  "message": "Invalid input",
  "code": "common.invalid_input",
  "class": "validation"
}
{
  "status": 401,
  "message": "Unauthorized",
  "code": "common.unauthorized",
  "class": "business"
}
{
  "status": 403,
  "message": "No access to the record",
  "code": "common.rbac_no_rec_access",
  "class": "business"
}
{
  "status": 429,
  "message": "Rate limit for 203.0.113.10 to 8f1d2c34-5b6e-4a70-9c81-2d3e4f5a6b7c exceeded.",
  "code": "rate_limits_m.exceeded",
  "class": "temporary",
  "retryable": true
}
{
  "status": 500,
  "message": "Invalid search field: status. Valid fields are: content, created_at, customer_id, event, id, read, read_at, rec_id, rec_type, sent_at, title, user_id",
  "code": "query_m.invalid_search_field",
  "class": "business"
}

{
  "overall_status": "unhealthy",
  "message": "Service is shutting down",
  "timestamp": "2026-08-27T15:04:05Z"
}