What this is
When we plan to take the trading API or withdrawals down, or take them down for an incident we can announce, we declare a maintenance window. You can find out two ways, and they are meant to be used together:- Poll
GET /api/platform/maintenancefor upcoming and active windows. Requires a partner API key. - Subscribe an address and be emailed when a window is scheduled, rescheduled, started, extended, completed or cancelled. Ask your account manager; see Email notifications.
GET /api/platform/status is
public and needs no key.
A window reports what we have declared, not what we have measured. A
window is the right signal for planned works and announced incidents. It is
not a health check — keep your own error handling for everything else.
Maintenance windows
X-Api-Key header — one we issued
when you were onboarded. No special scope is needed, any of your live keys
will do, and no X-User-Wallet header is needed: the schedule belongs to no
wallet.
Rate limits: 600 requests per minute per partner — all of your API keys share
that one bucket, and it scales with your partner rate tier like wallet limits
— plus 600 requests per minute per IP. Both buckets belong to this endpoint
alone and do not count against the limit the other
/api/platform/* routes
share. Responses carry Cache-Control: private, max-age=5, which matches
how fresh the data actually is — do not let an HTTP cache hold it longer
than that or you will read scheduled after the window has started.
Query parameters
Response
Polling incrementally
PassgeneratedAt back as your next from. Not the from you sent, and
not your own clock.
generatedAt is deliberately about a minute behind the moment we read the
data. A window’s end is stamped inside a transaction that commits a moment
later, and the lag guarantees that a completion or cancellation that was
mid-commit when we read still reaches you on the next poll. That means you
will be shown windows you already have for up to a minute after they
change — upsert by id, which is stable for the life of the window.
from in the response is only the filter that was applied to this request,
echoed for debugging. Using it as the cursor would move you past exactly the
transitions the overlap exists to protect, and a window that completed in
that gap would never appear again.
Fields
Three behaviours worth coding against
- A running window stays listed after
expectedEndAtpasses. Overrunning is exactly when you still need it. Drive your logic offstatus, never off the clock. - A window that is announced and never started disappears once its
expectedEndAtpasses (until then it stays listed asscheduled, even afterstartsAt). It is not going to happen as announced; do not keep waiting for it. Every response lists every window that is still due or running, so treat ascheduledwindow you hold that is missing from a response as gone. If we later reschedule, start or cancel it, it reappears with the new status. - A window that has started is never cancelled — it completes. Once you
see
in_progress, the only status that follows for thatidiscompleted.
Live status
The whole
maintenance object is absent in an environment where the
feature is switched off. Treat a missing object as “no information” — never as
“everything is fine”. The frozen / reason / since / activeNotices
fields are always present.maintenance.<surface>.windowId is the window explaining a maintenance
status — look it up in GET /api/platform/maintenance for the copy and the
expected end. It is null whenever no window explains the status: always
under degraded, because nobody declared it, and under maintenance when a
kill switch is on without an announced window. In both cases there is no
promised end to wait for.
Only trading is ever reported as degraded; it is the surface we measure.
withdrawals reports operational unless a window, the platform-wide
freeze, or the withdrawals-only freeze (503 withdrawals_frozen; frozen
stays false) covers it — so for withdrawals operational means “nothing
declared”, not “measured healthy”.
Treat
status as an open enum. More values may be added, so branch on
“is it operational” rather than matching the full set — anything else means
do not expect normal service.degraded signal is the same alert that pages our own on-call. It is
derived from the share of order placements answered with a 5xx over a
rolling five-minute window, re-checked every 30 seconds. It needs a minimum
number of failures as well as a minimum share, so a handful of errors cannot
trip it, and it clears only after a recovery hold of about 15 minutes, so it
does not flap.
Email notifications
Who is already on the list
Every partner that was active when this list was created was subscribed automatically, using thecontact_email we hold for you. API integrators
were subscribed to trading and withdrawals (which include full-platform
windows); partners that embed our UI were subscribed to all only —
full-platform windows. You do not need to ask for that.
Two things follow from it being automatic:
- If that address is a personal mailbox or an alias nobody watches, change it — see below. It was the only address we had.
- A partner onboarded after the list was created is not added automatically. If you joined recently and have never had one of these emails, ask.
How much mail to expect
At most one email per event, per subscribed address. A normal release is three —scheduled, started, completed — plus extended or
rescheduled only if something actually changes. Nothing is sent on a
repeat of the same transition, so a retry on our side does not produce a
duplicate. A notice that is already out of date when its turn comes is
dropped rather than sent late (for example, no started for a window that
has already completed), so a very short window can produce fewer emails. An
address that hard-bounces is taken off the list automatically.
What you hear about depends on which surfaces your address is subscribed to:
all is narrower than it sounds: it means “only tell me when the whole
platform goes down”. It is the right setting for an integration that embeds
our UI and never calls the trading API. If you want everything, subscribe to
trading and withdrawals — both already include full-platform windows.
What each email says
You are emailed when a window is:
Every email carries the window ID and a link to
GET /api/platform/status,
so an email you missed never leaves you without the current state. Upsert by
window ID on your side.
Adding, changing or removing an address
There is no partner-facing API for this, by design: the list decides who receives operational notices in our name, so changes go through us.
Tell us the mailbox your on-call actually watches, not a personal one.
Unsubscribing
Every email carries an unsubscribe link, and the endpoints behind it are public — the signed token in the link is the only credential. It stops mail to that address for the partner account the email was sent for; if the same mailbox is on the list for another account too, that subscription is unaffected.GET renders a confirmation page and changes nothing. That is deliberate:
corporate mail gateways and link-preview bots fetch every URL in a message
before a human sees it, and a GET that unsubscribed would take your address
off the list without anyone clicking.
POST performs it. That is both the button on the page and
RFC 8058 one-click, which is what
the List-Unsubscribe and List-Unsubscribe-Post headers on every email
advertise — so your mail client’s own “unsubscribe” works.
Unsubscribing is one-way: the token carries no re-subscribe bit, so a
leaked link can only stop mail. To go back on the list, ask.
Errors you may see during maintenance
The trading codes do not tell you why. That is what the maintenance window
is for.