Skip to main content

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:
  1. Poll GET /api/platform/maintenance for upcoming and active windows. Requires a partner API key.
  2. Subscribe an address and be emailed when a window is scheduled, rescheduled, started, extended, completed or cancelled. Ask your account manager; see Email notifications.
For “is it down right now”, 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

Requires a partner API key in the 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

Pass generatedAt 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 expectedEndAt passes. Overrunning is exactly when you still need it. Drive your logic off status, never off the clock.
  • A window that is announced and never started disappears once its expectedEndAt passes (until then it stays listed as scheduled, even after startsAt). 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 a scheduled window 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 that id is completed.

Live status

Public, no key. This is the endpoint to poll for “can I trade right now”.
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.
The 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 the contact_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.