## 2026-10-17

- Changed: an appointment made with [Create an appointment](/docs/api/calendar/create)
  takes the timezone of the account that makes it, and the calendar's only when
  the account has none. It always took the calendar's.

## 2026-10-16

- New: fraud for the Propper app. The [flags](/docs/api/admin/fraud-flags) of
  the Review queue with [a summary](/docs/api/admin/fraud-summary),
  [one flag](/docs/api/admin/fraud-flag) with its evidence and people,
  [deciding it](/docs/api/admin/fraud-flag-update) and
  [lifting a restriction](/docs/api/admin/fraud-restriction-lift); the
  [detectors](/docs/api/admin/fraud-detection) and
  [changing them](/docs/api/admin/fraud-detection-update); and
  [trader overrides](/docs/api/admin/fraud-overrides). New error code
  `override_exists`.
- Changed: the restrictions and overrides of a payout's
  [tab Fraud](/docs/api/admin/payout-fraud) carry the detector's name in
  `detector_name`.

## 2026-10-15

- New: the [admin endpoints](/docs/api/admin/overview) under `/v1/admin`, for
  the Propper app, starting with [the admin profile](/docs/api/admin/profile)
  and [the sidebar](/docs/api/admin/sidebar). They need a token signed in with
  `"app": "admin"` and the new scopes `admin:read` and `admin:write`, which no
  other app is granted.
- Signing in with `"app": "admin"` refuses an account that does not help run
  the environment, with `403 admin_access_required`.
- New error codes `workspace_required` and `platform_required`, and `forbidden`
  on an admin endpoint names the missing permission in `required_permission`.
- `locale` on [your profile](/docs/api/apps/profile) is null for an account that
  never chose a language. It always could be, the page now says so.
- [Push registration](/docs/api/apps/push-register) takes `"app": "admin"` for
  the Propper app, which gets the mail and calendar notifications as well.
- Signing in with `"app": "admin"` to an account that has to have two-factor
  and has none answers with
  [a setup step](/docs/api/apps/sign-in#response-two-factor-to-set-up) instead
  of `two_factor_setup_required`: [a secret](/docs/api/apps/two-factor-setup),
  then [the first code](/docs/api/apps/two-factor-setup-confirm), which signs
  in and hands out the recovery codes once.
- A signed-in app can [change its name and language](/docs/api/apps/profile-update),
  [its picture](/docs/api/apps/avatar-update) and
  [its password](/docs/api/apps/password), and
  [set up](/docs/api/apps/two-factor-enable) or
  [turn off](/docs/api/apps/two-factor-disable) an authenticator, with
  [its recovery codes](/docs/api/apps/recovery-codes).
- [Your profile](/docs/api/apps/profile) says which second factors you have, in
  `two_factor_methods`.
- An environment's branding carries `primary_foreground` and
  `primary_foreground_dark`, the colour the firm writes on its own colour.
- An environment's branding carries `icon_url`, a square of at most 512
  pixels made from the firm's favicon, for showing the firm in a small space.
- `reminders` on [an appointment](/docs/api/calendar/events) are only ever
  yours, as the page always said, and [a change](/docs/api/calendar/update)
  replaces only yours. On a shared calendar they used to be everybody's, and a
  change took the others' away.
- New: [what waits for you](/docs/api/admin/dashboard-attention), the
  dashboard's attention queue, with the new error code `invalid_kind`.
- New: [notification settings](/docs/api/apps/notification-settings), one
  switch per kind of notice, which silences the bell, the push and the mail of
  that kind at once. [Changing them](/docs/api/apps/notification-settings-update)
  answers with `invalid_kind` for a kind the account does not receive.
- New notices for admins when a payout waits to be reviewed or paid, an
  affiliate applies, asks for a code or asks for a payout: in
  [the notification centre](/docs/api/apps/notifications) and, on a device
  registered as `admin`, [as a push](/docs/api/apps/push-register#what-arrives)
  of the new type `admin.notification`. Every other notice for admins and for
  Propper's staff arrives that way too.
- New: the dashboard's [money](/docs/api/admin/dashboard-money),
  [what is owed to funded traders](/docs/api/admin/dashboard-owed) and
  [traders and accounts](/docs/api/admin/dashboard-traders), per period, with
  the new error code `invalid_period`.

## 2026-10-11

- A rotated key can keep its old secret working beside the new one for an
  hour, a day or a week, chosen by whoever rotates it. During that time a call
  with either secret is accepted. See
  [Rotating and revoking](/docs/api/authentication#rotating-and-revoking).
- An IP allowlist entry is checked when it is saved, so an entry that is not
  an IPv4 or IPv6 address or a CIDR range can no longer end up on a key and
  refuse every call.

## 2026-10-08

- New ids are 16 characters after the prefix instead of 24, such as
  `wl_k7m2p9x4q8r3t6v5`. Ids handed out before keep working. Treat an id as an
  opaque string and do not check its length.
- The calendar names calendars, appointments and labels by string ids such as
  `evt_k7m2p9x4q8r3t6v5` instead of numbers: `id`, `calendar_id` and the label
  ids in every response, `calendars[]`, `calendar_id` and `tag_ids[]` in
  requests, `/v1/me/calendar/events/{id}`, the `invitation` on a mail
  conversation and `event_id` in a reminder push. A number in a URL or in
  `calendars[]` finds nothing, and one in a body is refused with
  `validation_failed`.
- Calendars carry a `channel`, the socket channel to subscribe to for that
  calendar. Use it as it is rather than building one from `id`.
- Your profile and everything under `/v1/me/mail` name rows by string ids such
  as `msg_h3k8q2w7n4r9t5vx` instead of numbers: `id` on `/v1/me` and on every
  mailbox, label, signature, assignee, blocked sender, message, note and
  attachment, and every field that points at one, such as `thread_id`,
  `address_id`, `mailbox_id`, `author_id`, `user_id`, `reply_to_id`,
  `signature_id`, the label ids, `assignee`, `answered`, `deleted` in the sync
  and both sides of `meta.defaults` on the signatures. The prefix says what an
  id names, see [Reading mail](/docs/api/mail/overview). The `public_id`,
  `author_public_id` and `user_public_id` fields are gone, because `id`,
  `author_id` and `user_id` now carry the same value.
- Requests take those ids as well: every path under `/v1/me/mail`, the
  `mailbox` and `label` filters, `label_id` and `user_id`, and `address_id`,
  `in_reply_to`, `draft_id` and `signature_id` on a send, a draft and the
  writing help. A number in a path or in the `label` filter answers `404`, one
  in the `mailbox` filter is ignored like any mailbox you cannot read, and one
  in a body is refused with `validation_failed`.
- Your profile and each mailbox carry a `channel`, the socket channel your
  notifications and that mailbox's new mail arrive on. Use it as it is rather
  than building one from `id`.
- The `channel` on your profile names you by your string id, such as
  `private-App.Models.User.usr_hraj4wtnqgrkeu9b`, instead of a number.
  `/v1/me/broadcasting/auth` refuses the old name with `channel_not_allowed`,
  so subscribe to the value `/v1/me` gives you now. The `channel` of each
  mailbox changes the same way, such as
  `private-mail-address.mbx_w6sy3skvgydh5e3b`.
- The `channel` of each calendar names it by its string id too, such as
  `private-calendar.cal_r4d8h2m6t9w3k7nb`, and the old name is refused the same
  way. `calendar.changed` carries that id as `calendar_id`, so it now matches
  the `id` in the calendar list.
- Notifications on that channel no longer carry `user_token_id`, `ticket_id`,
  `fraud_flag_id` or `tenant_id`. They were internal numbers that matched
  nothing else in this API.
- `mail.received` names the message, its conversation and its mailbox by the
  same string ids, and so do push notifications: `user_id` in a mail or
  reminder push, and `message_id`, `thread_id` and `mailbox_id` in a mail push.
  Mail notifications still stack per mailbox, under a `thread-id` that now
  carries the mailbox's string id.
- A list cursor or a sync `next_since` from before this change is refused with
  `invalid_cursor` or `invalid_since`. Rebuild a cached mailbox from the list:
  every id in it was a number.
- Workspaces keep their slug, or `platform`, as `id`. They never carried a
  number, so the `Propper-Workspace` header and `workspace` in a push stay as
  they were.

## 2026-08-04

- Added the launch waitlist: create, list, retrieve, resend and unsubscribe under
  `/v1/waitlist/signups`.
- Added the `waitlist:read` and `waitlist:write` scopes. Existing keys do not
  have them; tick the boxes on the key that needs them.

## 2026-08-03

First release of `v1`.

- `GET /v1/ping` for verifying a credential.
- Affiliate pre-registration: create, list, retrieve, resend and withdraw.
- API keys, scopes, idempotency, cursor pagination and rate limits.