POST
/
api
/
v1
/
me
/
push-tokens
Register push token
curl --request POST \
  --url https://api.example.com/api/v1/me/push-tokens
import requests

url = "https://api.example.com/api/v1/me/push-tokens"

response = requests.post(url)

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

fetch('https://api.example.com/api/v1/me/push-tokens', 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/push-tokens",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);

$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/push-tokens"

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

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

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

fmt.Println(string(body))

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

url = URI("https://api.example.com/api/v1/me/push-tokens")

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

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

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

Overview

The client posts its FCM registration token on first launch (after the user grants notification permission) and again whenever FCM rotates the token. Calling this endpoint with a token that’s already on the user’s record refreshes its updatedAt (and re-orders it to “most recent”) — it does not insert a duplicate row. Per QUESTIONS.md §7.1, all tokens are treated as FCM registration tokens by the worker-side PushSender, regardless of the platform field. The platform is retained for analytics — iOS clients deliver their APNs payload through the same FCM pipeline (FCM forwards to APNs server-side).
Token storage is capped at 10 active tokens per user. When a registration would push the count above 10, the oldest token by updatedAt is evicted. This bounds the array size on the user document and matches typical “5–6 active devices per user” patterns observed at scale.

Authentication

Bearer <accessToken> required. requireAuth + requireOnboarded middleware applied.

Path parameters

None.

Query parameters

None.

Request body

FieldTypeRequiredNotesExample
tokenstringyes1..4096 chars. The FCM registration token issued to the device by getToken() (or its native equivalent).fcm_AAAA...truncated
platformenumyesios or android (lowercase on the wire; stored as IOS/ANDROID in Mongo). Retained for analytics only — see callout above.ios

Example payload

{
  "token": "fcm_eYqJ...VeryLongFCMRegistrationToken...",
  "platform": "ios"
}

Response — 200 OK

{ "success": true }

Side effects

  • Upserts the (token) entry in the user’s pushTokens array with platform, createdAt (preserved on refresh), and updatedAt (always set to now).
  • If inserting would push the array above 10 entries, removes the oldest by updatedAt first.
  • Does NOT broadcast or enqueue anything else. The token only takes effect on the next push attempt.

Error responses

StatusCodeMeaning
400VALIDATION_FAILEDtoken missing/empty/over 4096 chars, or platform not in { ios, android }.
401UNAUTHENTICATEDMissing, malformed, or expired access token.
403ONBOARDING_INCOMPLETECaller has not finished onboarding.

Example error — 400 VALIDATION_FAILED

{
  "type": "https://api.swappr.co.uk/errors/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "code": "VALIDATION_FAILED",
  "detail": "Request body failed validation",
  "instance": "/api/v1/me/push-tokens",
  "requestId": "01HZQ7K3M4N5P6Q7R8S9T0V1W2",
  "errors": [
    { "path": "platform", "message": "Invalid option: expected one of \"ios\"|\"android\"", "code": "invalid_value" }
  ]
}

See also

curl

curl -X POST https://api.swappr.co.uk/api/v1/me/push-tokens \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "fcm_eYqJ...VeryLongFCMRegistrationToken...",
    "platform": "ios"
  }'