Register for notifications

.md

Tell the server where this app on this phone gets its notifications.

POST/v1/me/devices

Stores the push token Apple handed the app, so the server can send it notifications. One registration per app per signed-in token: calling this again, for instance when iOS hands out a new push token, replaces the one before.

Needs no scope, the same as signing out. What actually reaches the phone is still decided by the scopes of the token when there is something to send: a token without mail:read gets no mail notifications, registered or not.

Signing out ends the registration with it. So does a password change, which signs out every other device.

Request

curl -X POST https://api.proppertrading.com/v1/me/devices \
  -H "Authorization: Bearer ptat_9f2c1a44_3b8e7d2f5c9a1b4e6d8f0a2c4e6b8d1f" \
  -H "Idempotency-Key: 5b0e8c1c-9f0e-4d3e-a3b1-2f7c8d9e0a1b" \
  -H "Content-Type: application/json" \
  -d '{
    "app": "mail",
    "environment": "production",
    "token": "8f3b2c1d9e0a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c",
    "locale": "nl",
    "timezone": "Europe/Amsterdam",
    "app_version": "0.1.28",
    "os_version": "27.0"
  }'
FieldDescription
appmail, calendar or admin. Mail and the calendar share a token on a phone and register separately, because each has its own bundle id
environmentsandbox for a development build, production for TestFlight and the App Store. A token sent to the other one is refused by Apple as if it did not exist
tokenThe device token as hex
localeOptional. Not used for the words yet: those follow the account's language
timezoneOptional, an IANA zone. A calendar reminder says the time on this clock

Response

201 for a new registration, 200 for one that replaced the last.

{
    "data": {
        "object": "push_device",
        "app": "mail",
        "environment": "production",
        "created_at": "2026-09-26T09:12:44.000000Z",
        "last_seen_at": "2026-09-26T09:12:44.000000Z"
    },
    "meta": {
        "request_id": "req_01kz9d2m3n4p5q6r7s8t9v0w1x"
    }
}

What arrives

Every notification carries, beside the aps dictionary, a type and the account it is for, so an app with several accounts opens the right one: session is the id of the signed-in device it was sent to, the same id as in the sessions list.

typeExtra fields
mail.messageuser_id, workspace, message_id, thread_id, mailbox_id
calendar.reminderuser_id, workspace, event_id, occurrence_start, conference_url
admin.notificationuser_id, workspace, kind, notification_id, action_url
testnone

Every id in these fields is a string. Compare it with the ids the API hands out as it is, and never parse it as a number.

user_id is the id from your profile, such as usr_hraj4wtnqgrkeu9b. workspace is the id from the environments list: a propfirm's slug, or platform.

message_id, thread_id and mailbox_id are the id, thread_id and address_id of the message as a list row carries them, such as msg_h3k8q2w7n4r9t5vx, so message_id is what opening it takes. event_id is the id of the appointment as the calendar hands it out.

Mail notifications are stacked per mailbox: the thread-id in aps is mailbox- followed by the mailbox's id.

admin is Propper, which carries Mail and the calendar inside it. A device registered as admin gets the mail and calendar notifications too, as long as its token holds the scope to read them.

admin.notification goes to admin devices only, and is a notice from the notification centre on its way to the phone as well, in the same words: notification_id is its id there, kind its type and action_url the same path. workspace is the environment the reader runs, a propfirm's slug for its admins and platform for Propper's own staff. These kinds arrive this way:

kindWhen
payout_requestedA payout waits to be reviewed, asked for or a cash prize
payout_awaiting_paymentA payout was approved and waits to be paid
affiliate_application_receivedA trader applied to become an affiliate
affiliate_code_requestedAn affiliate asked for a code that has to be reviewed
affiliate_payout_requestedAn affiliate asked for their commission
risk_flag_raisedA fraud detector raised a flag
ticket_awaiting_adminA support ticket waits for a person
storage_filling_upThe environment is running out of storage
mail_deliverabilityThe environment's mail bounces or draws complaints too often
new_device_signed_inSomebody signed in to your account on a new device
deal_awaiting_approvalStaff: a new propfirm paid and waits for approval
billing_payment_failedStaff: a propfirm's invoice failed to collect
deal_link_expiredStaff: a propfirm's onboarding link expired unused
analytics_healthStaff: an environment's traffic looks wrong

The first five go to the admins of the environment who may see that kind on the dashboard, apart from whoever caused it. The rest go to whoever the bell gives them to. An app should open action_url when it knows the screen and fall back to the web version when it does not, as for the notification centre.

Mail notifications go for mail landing in an inbox, to everybody who may read that mailbox and has not switched it off, apart from its sender. The archive, spam and the rest stay quiet. They show sender and subject, unless the propfirm chose notifications without content. The badge is the unread count across the mailboxes this account can read in that environment.

Errors

StatusCodeMeaning
401invalid_tokenNot a token, or the secret is wrong
422validation_failedA field is missing or has a bad shape