Changelog

Two things this file does not decide

What counts as a breaking change, and how much notice a deprecation gets. Both have commercial weight — they are promises to a partner, not repository conventions — so they are left open on purpose rather than filled in with a plausible-looking default. They should be answered before the first partner integrates, because after that the answer stops being free.

Until they are, treat every entry below as descriptive: it says what changed, not what a partner was entitled to expect.

2.6.0

GET /aum/summary?groupBy=client now names the advisor, and splits rows to do it.

The client group key gains four fields: advisorId, advisorName, partnerName and partnerRegistrationNumber. The last is the same value as AdvisorPartner.registrationNumber — an IČO for partners that are legal entities, a birth number for the natural persons who make up most of the network, told apart by length. Getting the advisor and their identifier alongside a client's AUM no longer needs a second call per client.

This changes the number of rows you get, and it is the one thing to check before you upgrade. The advisor is part of the key, so a client holding accounts with two advisors now produces two rows where it produced one, and those rows' aum values sum to that client's total. If you were treating clientId as unique within groups, it no longer is. Group by clientId yourself to get the old shape back.

There was no honest alternative: one row cannot name two advisors, and picking one of them would have attributed the whole of a client's AUM to whichever the grouping happened to land on.

totals is unchanged — accounts and clients still count the book, not the groups, so totals.clients no longer equals groups.length for an organization that has such a client. The advisor variant of the key is untouched.

2.5.0

New: partner on both advisor resources. Corrected: what registrationNumber actually holds.

GET /advisors and GET /advisors/{advisorId} now return a partner object with partnerId, code, name and registrationNumber. It describes the business subject an advisor operates under. partner previously existed on the detail response with partnerId alone; it is now the same four-field object on both, so the list gives you the whole mapping in one paged call instead of one call per advisor.

registrationNumber is not a regulator's reference number. Until this version the field was described as a "Regulatory registration identifier" and its example read REG-12345. Neither was true of the data behind it, and the gap mattered: of our 6,854 advisors, 181 carry an 8-character IČO and 6,671 carry a 9- or 10-character birth number (rodné číslo, written without the slash). partner.registrationNumber has the same dual nature — 920 of 7,315 partners carry an IČO, 6,394 a birth number. Both fields now say so, and both say how to tell the two apart: by length.

No value changed. What changed is that the contract stopped describing these fields as something they are not, and the examples were replaced with deliberately impossible values so that nobody mistakes a sample for a real person's identifier.

Both fields are returned in full regardless of piiAccess, and both now say so. That differs from ClientDetail.personalNumber, which is withheld from callers without it. The distinction is the role the value plays rather than its format: these identify the advisor and the subject you contract with, and are the key you join our data on. A partner otherwise has to parse the code out of a display string to get at it, which is what prompted this change.

Two knock-on notes for anyone already integrated:

— username and partner.code carry the same code that prefixes name and AccountSummary.advisor.name. If you have been slicing that prefix out of the display string, you can stop — and you should, because the prefix is not an IČO even where it is eight digits long.

— partner.name is a business name. For a company it is the company's name, not the advisor's, and the two differ for a substantial minority of advisors. Do not use it as "the advisor's name without the code".

2.4.1

Documentation only — no field, value or behaviour changed. What changed is that the contract now says something it had been silently assuming.

Withdrawal dates are Europe/Prague calendar dates; the deposit resources' dates are UTC. Both were always so; neither said which. The two resources therefore do not share a calendar, and that only shows up when you put them in one monthly figure — net flow, deposits minus withdrawals per month, being the obvious one. Read as UTC instants, 9,660 of 81,905 withdrawals (11.8%) fall in a different month than the one /withdrawals dates them to. They are not scattered around midnight either: they cluster on orders created just after local midnight on the first of the month, so a mismatched calendar moves whole batches at a time rather than a few edge cases.

Aggregate each resource on its own dates, then combine the monthly totals.

/withdrawals also states explicitly that it publishes no time of day, and why: the underlying timestamp is written by a batch on roughly a third of orders — hundreds can share one value — so it would not be the moment the client acted. If that changes upstream we will say so here.

2.4.0

Two corrections to what the cash-flow resources promise. Neither changes a field or a value; both change what you are entitled to rely on, and one of them in your favour.

paymentId is durable once its month has closed. 2.3.0 said it was stable only within a single load and told you not to use it as a long-lived key. That was too pessimistic: the id derives from the position a payment holds in the source record, not from a numbering recomputed on each rebuild. Measured across four rebuilds, it never changed for a month that had already closed — 5 payments in 1.59 million changed at all, every one of them inside the month still running.

So you can store paymentId and match on it later for any month that /deposit-fulfilment reports as periodComplete: true. For the current month treat it as provisional, which is the same advice that month already carries for its figures.

dataAsOf is the age of the data, not the time we fetched it. The description now says so. The value was always derived from the warehouse rather than from the request, but on these three resources it had been reporting when our own copy ran, which is nightly — while the warehouse data it copies is rebuilt weekly. A response could therefore claim data was hours old when it was days old, and would have said "yesterday" through an upstream outage lasting a week.

It now reports when the warehouse last rebuilt the data. Expect it to sit several days behind the moment you call; that gap is real and was previously hidden. If you have a freshness threshold in your integration, re-check it against the new values before assuming something broke.

2.3.0

Added — deposits as they arrived, and withdrawals

Three resources, all additive; nothing existing changed.

— GET /accounts/{variableSymbol}/deposit-payments — one entry per payment that actually arrived on the account.

— GET /accounts/{variableSymbol}/deposit-fulfilment — month by month, what was planned against what arrived, and where the received money was applied.

— GET /accounts/{variableSymbol}/withdrawals — withdrawal orders in every state.

/deposits is unchanged and still means the plan. It describes what a client arranged to send; deposit-payments describes what turned up. They are different resources at different grains and neither replaces the other — a plan has no payments and a payment may have no plan.

Read these three caveats before building on the data. Each is a property of the source, is stated in the endpoint descriptions, and will not be fixed by us:

— paymentDate is null for every payment before March 2026, because the application did not record it. Daily series are possible from 2026-03; before that, period (a month) is the only granularity available. from/to therefore select whole months.

— amount on a withdrawal can be null — about one in ten executed orders carries no amount at all, flagged by amountUnknown. Totals computed from this resource are lower bounds.

— The fulfilment entry for a month still in progress is rewritten as payments arrive, so received is a running total and state can still change. periodComplete marks it. Excluding an unfinished month from a comparison is the difference between a chart that reads correctly and one whose last point looks like a collapse.

Also: planBreakdownAvailable is false for months before November 2023, and the four plan breakdown fields are then null rather than zero — the split was not recorded back then, and zero would have asserted that the client planned nothing.

paymentId is opaque and stable only within one load of the underlying data; it derives from the source's ordering of payments inside a month, and that ordering is reassigned on every rebuild. It is not a long-lived key. withdrawalId and the fulfilment entries have no such caveat.

Freshness differs from the monthly resources. These three are rebuilt weekly by the warehouse rather than monthly, and their dataAsOf is a timestamp of the last load rather than a snapshot date.

2.2.0

The advertised base URLs changed. servers: now lists https://partnerapi.edwardinvest.cz/v1 and https://sandbox.partnerapi.edwardinvest.cz/v1, replacing https://api.edward.cz/v1 and https://sandbox.api.edward.cz/v1.

Nothing about the API's behaviour changed, and no integration breaks: neither of the previous hostnames was ever provisioned, so no client could have been calling them. The new names are the ones decided on 2026-08-22 and the ones the gateway will answer on.

The development gateway answers on https://dev.partnerapi.edwardinvest.cz/v1 today. That host is deliberately not in servers: — it is an environment, not a published address. Ask Edward Partner Integrations which URL applies to you.

Unchanged: error type URIs still use the https://api.edward.cz/errors/ namespace. Per RFC 9457 a problem type is an identifier rather than a location, and it is not required to dereference. Do not treat a change of base URL as a change of error type.

2.1.0

Added — granularity=MONTHLY is served on account performance

GET /accounts/{variableSymbol}/performance?granularity=MONTHLY returns a monthly series. The contract has offered the value in the Granularity enum since 1.0.0, but the endpoint answered it with 400 granularity 'MONTHLY' is not currently available, because no monthly row existed in the model — every point was WEEKLY. The rows are now produced by the sync, and the rejection is gone.

What a monthly point is, in short (the Granularity schema says it in full):

· it carries the state at the last snapshot within that month, not at the month's last calendar day — the underlying snapshots are one to seven days apart and rarely land on it;

· its period is the first day of the month the point summarises, as a label for the month;

· only closed months appear, so the last point is never the month in progress;

· mwr and twr are the inception-to-date figures carried over from that snapshot, not returns earned during the month, while changePct is recomputed month over month and is null where the preceding month has no point.

The weekly series is untouched: same points, same values, same default. A request that does not name a granularity still gets WEEKLY.

Why 2.1.0 rather than 3.0.0

The change is additive. MONTHLY previously always failed, so no working client can be relying on that failure, and nothing a client already reads has changed shape or meaning.

2.0.0

Removed — advisor standing is no longer served

GET /advisors/{advisorId}/status is withdrawn, together with the AdvisorStatusDetail, AdvisorStatusCode and AdvisorAumTier schemas. The endpoint returned Edward's own evaluation of a partner's advisors — the ELITE / ON_FIRE / ACTIVE / NEWBIE gamification tier, the AUM band, the advisor's rank within the organization, and the monthly history of all three.

The decision is the business owner's, recorded internally on 23 July 2026: the internal advisor rating and ranking should not be served through this API at all, and no data behind that ranking should be exposed. Nothing technical forced it and nothing was broken; the data was serviceable and the endpoint worked.

The contract now publishes sixteen operations rather than seventeen.

What was deliberately left in place

· rank on AumSummary.groups[] stays. It reads as a leaderboard, but it is index + 1 over groups already filtered to the caller's own intermediaryCode and ordered by an AUM figure the same response returns in full — a caller can reproduce it by sorting. Removing it would cost a convenience and protect nothing. This is the API's own arithmetic over the partner's own book, not Edward's assessment of their people, which is the distinction the decision turns on.

· The advisor-status history and AUM-history tables and their sync sources still run. Not serving the data is honoured at the API surface, which is where a partner can reach; dropping the underlying tables is a migration with no reader left to serve, and is reversible in the wrong direction.

Why 2.0.0 rather than 1.1.0

Removing an operation is the least ambiguous breaking change a contract can make. This file still declines to define what counts as breaking in general, and no partner is integrated against 1.0.0 today — but recording a removal as a minor bump would repeat the exact defect the changelog was created to stop.

1.0.0

Everything published so far, and that is the problem this file exists to stop repeating.

Thirty commits touched the contract in the weeks before this changelog existed, and info.version read 1.0.0 for all of them. Some of those changes would have been breaking for anyone already integrated:

· an enum narrowed from ten values to eight,

· five sources moved to native-currency-only amounts,

· a date-time field turned into a date.

It was harmless only because no partner was integrated. A partner holding an older copy of the contract's rendered documentation could not tell it was older: every copy carried info.version: 1.0.0 and an identical info.title.

The substantive changes, grouped by what they did to the contract:

Corrections — the contract described behaviour the API does not have

· portfolioType no longer forbids the null the API actually returns.

· 403 documented as the response the API sends, replacing a description of one it never sends.

· personalNumber documented as withheld rather than masked, matching what the API does.

· Portfolio weights no longer promised to sum to 100%.

· fxRate no longer guaranteed where two sources leave it null.

· Advisor history documented with the condition under which it appears.

· updatedSince documented as filtering on sync time rather than change time.

· The rate limit described as the token bucket the gateway runs, not a rolling window.

Additions — the contract was silent where the API is not

· required declared on 27 schemas and on PageInfo.

· 500 declared on all 17 operations at the time; 400 declared on /advisors.

· Problem types enumerated, with the current RFC cited.

· Response examples added to all 17 operations at the time.

· CSV columns listed instead of being called "the same data".

· Cursor semantics and end-of-paging explained.

· Performance defaults declared rather than illustrated.

· Fields not yet supplied marked as such, instead of implying data.

· The AUM group key described instead of left open-ended.

· Closed enums that a pass-through sync can overrun now say so.

Behaviour changed to match the contract

· MONTHLY and asOf rejected with 400 instead of answered with silent zeros.

· status and bucket rejected when outside their declared enums.

This page mirrors the contract's changelog, which is maintained alongside the contract itself. If this page and a rendered contract you hold disagree, the version in the contract's info.version is the one that decides.