curl --request GET \
--url https://{host}/v1/me/notification-preferences \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://{host}/v1/me/notification-preferences', 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://{host}/v1/me/notification-preferences",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}import requests
url = "https://{host}/v1/me/notification-preferences"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text){
"data": {
"type": "notification-preferences",
"id": "4711",
"attributes": {
"categories": {
"messages": {
"push": true,
"mail": true
},
"offers": {
"push": true,
"mail": true
},
"saved_searches": {
"push": true,
"mail": true
},
"price_drops": {
"push": false,
"mail": true
},
"orders": {
"push": true,
"mail": true
},
"shop": {
"push": true,
"mail": true
},
"security": {
"push": true,
"mail": true
}
},
"defaults": {
"messages": {
"push": true,
"mail": true
},
"offers": {
"push": true,
"mail": true
},
"saved_searches": {
"push": true,
"mail": true
},
"price_drops": {
"push": true,
"mail": true
},
"orders": {
"push": true,
"mail": true
},
"shop": {
"push": true,
"mail": true
},
"security": {
"push": true,
"mail": true
}
},
"labels": {
"messages": {
"label": "Messages",
"description": "Somebody replied in one of your conversations.",
"locked": [],
"lockedNote": null,
"managedPerSearch": false
},
"offers": {
"label": "Offers",
"description": "An offer was accepted, refused, countered, or made to you.",
"locked": [],
"lockedNote": null,
"managedPerSearch": false
},
"saved_searches": {
"label": "Saved searches",
"description": "A saved search found something new. How often is set per search.",
"locked": [
"push",
"mail"
],
"lockedNote": null,
"managedPerSearch": true
},
"price_drops": {
"label": "Price drops and favorites",
"description": "A watch you saved got cheaper, was reserved or was sold.",
"locked": [],
"lockedNote": null,
"managedPerSearch": false
},
"orders": {
"label": "Orders",
"description": "Your payment was recognised, or your watch is on its way.",
"locked": [
"push",
"mail"
],
"lockedNote": "Always on: you need these to stay updated on your orders.",
"managedPerSearch": false
},
"shop": {
"label": "Your shop",
"description": "A buyer wrote to your shop, made an offer, or paid for a watch.",
"locked": [],
"lockedNote": null,
"managedPerSearch": false
},
"security": {
"label": "Account security",
"description": "A sign-in, or a change to your password or second factor.",
"locked": [
"push",
"mail"
],
"lockedNote": "Always on, by email and in the app. This is how you find out somebody else changed your account.",
"managedPerSearch": false
}
}
}
}
}{
"errors": [
{
"status": "404",
"code": "watch_reserved",
"title": "Not Found",
"detail": "<string>",
"meta": {},
"source": {
"pointer": "/data/attributes/reference",
"parameter": "<string>"
}
}
],
"jsonapi": {
"version": "1.0"
}
}Read notification preferences
Scope: account:read
One document rather than a resource per switch: a settings screen draws the whole grid and saves the whole grid.
Every category comes back, including the ones nobody has decided about — those are their defaults. Draw the screen from this answer rather than from a list in the app, and a category we add appears without a release.
categories is each category’s push and mail as they stand for this person.
defaults is what every switch would be if nobody had decided anything, so a “restore
defaults” button can tell whether it would change anything. labels carries what to
call each one and what it means, translated by Accept-Language, plus locked: the
channels that cannot be switched off, so a client draws them as fixed rather than as
broken, and lockedNote: why a locked category is always on, translated, to show where
its switches would be (null for a category that can be switched off), and
managedPerSearch: true for saved_searches, whose switches are each search’s own
— draw a button to the saved searches instead of switches, locked or not.
Both of security’s channels are locked. “Your password was changed” going to an
address somebody no longer controls is their last warning, and a switch that can silence
it is a switch an attacker can silence.
Both of orders’ channels are locked as well: somebody who paid for a watch needs to
hear that it shipped or that the payment failed. Locked here does not mean the push
always arrives — that still depends on the phone allowing notifications — so its
lockedNote does not promise it.
Both channels mean what they say. mail: false stops the mail of that category, on
the website and the app alike, and push: false the push. The bell (/v1/me/notifications)
still records everything either way: it is the list somebody opens to see what they missed.
What each category’s mail is:
| Category | |
|---|---|
messages | The digest of replies a buyer has not seen yet |
offers | — (offers arrive in the messages digest) |
saved_searches | New matches, at the search’s own interval. Set per search, not here (managedPerSearch). |
price_drops | A favourite got cheaper. Also governs the reserved/sold pushes, which have no mail. |
orders | Order confirmed, shipped, delivered, cancelled; the payment reminder; the on-request reminder; the bank-transfer instructions. Locked on. |
security | Password, email address, second factor or sign-in method changed; a sign-in from a new device. Locked on. |
shop | The digest of what buyers wrote to the shop, and “your watch was sold” — to the staff member each is addressed to |
Everything is on to begin with, on both channels, shop push included: it only
reaches people who work at a shop. Do not assume the defaults; read the answer,
defaults included.
The security events are a password change, an email change, a second factor going on or off, a sign-in method (Apple, Google) linked or unlinked, and a sign-in from a new device. They send a mail as well, which is the channel that reaches somebody from outside the app — and in the case these exist for, a registered phone may not be theirs any more.
Saved searches are not switched here. Each search has its own interval for the mail
and push for the phone (/v1/me/saved-searches), and those alone decide.
saved_searches is therefore locked on both channels and comes back true, with
labels.saved_searches.managedPerSearch: true; its lockedNote is null, because it is
not always on. A false stored for it from before is ignored, so nobody stays muted by a
switch the screens no longer show.
security and orders cannot be turned off, on either channel: their
labels.{category}.locked is [push, mail]. Draw both switches as fixed, with
labels.{category}.lockedNote underneath. A false stored for orders before it was
locked is ignored: they are sent regardless. Quiet hours are gone; a quietHours field
sent by an older build is ignored, and the categories next to it are saved as usual.
The website shows the same switches (My account → Notifications) and writes the same stored choices, so a change on either side is the other’s too.
curl --request GET \
--url https://{host}/v1/me/notification-preferences \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://{host}/v1/me/notification-preferences', 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://{host}/v1/me/notification-preferences",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}import requests
url = "https://{host}/v1/me/notification-preferences"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text){
"data": {
"type": "notification-preferences",
"id": "4711",
"attributes": {
"categories": {
"messages": {
"push": true,
"mail": true
},
"offers": {
"push": true,
"mail": true
},
"saved_searches": {
"push": true,
"mail": true
},
"price_drops": {
"push": false,
"mail": true
},
"orders": {
"push": true,
"mail": true
},
"shop": {
"push": true,
"mail": true
},
"security": {
"push": true,
"mail": true
}
},
"defaults": {
"messages": {
"push": true,
"mail": true
},
"offers": {
"push": true,
"mail": true
},
"saved_searches": {
"push": true,
"mail": true
},
"price_drops": {
"push": true,
"mail": true
},
"orders": {
"push": true,
"mail": true
},
"shop": {
"push": true,
"mail": true
},
"security": {
"push": true,
"mail": true
}
},
"labels": {
"messages": {
"label": "Messages",
"description": "Somebody replied in one of your conversations.",
"locked": [],
"lockedNote": null,
"managedPerSearch": false
},
"offers": {
"label": "Offers",
"description": "An offer was accepted, refused, countered, or made to you.",
"locked": [],
"lockedNote": null,
"managedPerSearch": false
},
"saved_searches": {
"label": "Saved searches",
"description": "A saved search found something new. How often is set per search.",
"locked": [
"push",
"mail"
],
"lockedNote": null,
"managedPerSearch": true
},
"price_drops": {
"label": "Price drops and favorites",
"description": "A watch you saved got cheaper, was reserved or was sold.",
"locked": [],
"lockedNote": null,
"managedPerSearch": false
},
"orders": {
"label": "Orders",
"description": "Your payment was recognised, or your watch is on its way.",
"locked": [
"push",
"mail"
],
"lockedNote": "Always on: you need these to stay updated on your orders.",
"managedPerSearch": false
},
"shop": {
"label": "Your shop",
"description": "A buyer wrote to your shop, made an offer, or paid for a watch.",
"locked": [],
"lockedNote": null,
"managedPerSearch": false
},
"security": {
"label": "Account security",
"description": "A sign-in, or a change to your password or second factor.",
"locked": [
"push",
"mail"
],
"lockedNote": "Always on, by email and in the app. This is how you find out somebody else changed your account.",
"managedPerSearch": false
}
}
}
}
}{
"errors": [
{
"status": "404",
"code": "watch_reserved",
"title": "Not Found",
"detail": "<string>",
"meta": {},
"source": {
"pointer": "/data/attributes/reference",
"parameter": "<string>"
}
}
],
"jsonapi": {
"version": "1.0"
}
}