GET
/
api
/
v1
/
me
/
notifications
Notifications feed
curl --request GET \
  --url https://api.example.com/api/v1/me/notifications
import requests

url = "https://api.example.com/api/v1/me/notifications"

response = requests.get(url)

print(response.text)
const options = {method: 'GET'};

fetch('https://api.example.com/api/v1/me/notifications', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/api/v1/me/notifications",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"net/http"
"io"
)

func main() {

url := "https://api.example.com/api/v1/me/notifications"

req, _ := http.NewRequest("GET", url, nil)

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.get("https://api.example.com/api/v1/me/notifications")
.asString();
require 'uri'
require 'net/http'

url = URI("https://api.example.com/api/v1/me/notifications")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)

response = http.request(request)
puts response.read_body

Overview

The in-app notification feed shown on the Notifications screen. Every event that wakes the device with a push (a new mutual match, new chat messages, a listing going live, tenancy status changes) also writes a persistent notification row for the recipient, so the feed and the push stay in sync. Each item carries a read flag. The client groups unread items under “New” and read items under “Earlier” (matching the Figma). unreadCount powers the bell badge on the home header.
Notifications are written best-effort alongside the domain write — a match/message is never rolled back if the feed write fails. Conversely, the global notification mute suppresses push delivery only; in-app notifications are still recorded so the user sees them when they open the app.

Notification types

typeMeaningdata deep-link
MATCH_NEWA new mutual match.matchId
MESSAGE_NEWA new chat message.conversationId, peerUserId
LISTING_LIVEThe user’s listing went LIVE — visible to matches (onboarding complete AND tenancy approved).
TENANCY_RECEIVEDTenancy proof received / under review.
TENANCY_APPROVEDTenancy verification approved.
TENANCY_REJECTEDTenancy verification rejected — re-upload needed.
SYSTEMGeneric / ops message.

Authentication

Bearer <accessToken> required on every route. requireAuth + requireOnboarded applied.

GET /me/notifications — list

Newest-first, cursor-paginated by createdAt.

Query parameters

ParamTypeRequiredNotes
limitintegernoPage size, 1..50. Default 20.
beforestringnoOpaque cursor — pass the previous response’s nextCursor to fetch the next (older) page.

Response — 200 OK

FieldTypeNotes
items[]arrayThe notifications for this page.
items[].idstringNotification id.
items[].typestringOne of the types above.
items[].titlestringBold headline (e.g. New Mutual Match!).
items[].bodystringSupporting line(s).
items[].dataobjectDeep-link context (matchId / conversationId / peerUserId).
items[].readbooleanfalse → render under “New”.
items[].createdAtstringISO timestamp; format relative client-side (“2 minutes ago”).
nextCursorstring | nullPass as before for the next page; null when exhausted.
unreadCountintegerTotal unread for the bell badge.
{
  "items": [
    {
      "id": "65f0c2a1b3e4d5f6a7b8c9d0",
      "type": "MATCH_NEW",
      "title": "New Mutual Match!",
      "body": "You have a new mutual match. Tap to view details.",
      "data": { "matchId": "65f0..." },
      "read": false,
      "createdAt": "2026-06-07T09:58:00.000Z"
    },
    {
      "id": "65f0c2a1b3e4d5f6a7b8c9d1",
      "type": "MESSAGE_NEW",
      "title": "New message",
      "body": "Saturday would be brilliant! How about 2pm?",
      "data": { "conversationId": "65ef...", "peerUserId": "65ee..." },
      "read": false,
      "createdAt": "2026-06-07T09:00:00.000Z"
    }
  ],
  "nextCursor": "2026-06-07T09:00:00.000Z",
  "unreadCount": 2
}

POST /me/notifications/:id/read — mark one read

Idempotent. Returns { "updated": boolean } (false if it was already read or not found). 200 OK.

POST /me/notifications/read-all — mark all read

Returns { "count": <number marked> }. 200 OK.

DELETE /me/notifications/:id — delete one

Idempotent. Returns { "removed": boolean }. 200 OK.

DELETE /me/notifications — clear all

Deletes every notification for the user. Returns { "count": <number removed> }. 200 OK.

Error responses

StatusCodeMeaning
400VALIDATION_FAILEDlimit out of range (1..50).
401UNAUTHENTICATEDMissing, malformed, or expired access token.
403ONBOARDING_INCOMPLETECaller has not finished onboarding.

See also

curl

# List
curl https://api.swappr.co.uk/api/v1/me/notifications?limit=20 \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# Mark one read
curl -X POST https://api.swappr.co.uk/api/v1/me/notifications/$ID/read \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# Clear all
curl -X DELETE https://api.swappr.co.uk/api/v1/me/notifications \
  -H "Authorization: Bearer $ACCESS_TOKEN"