Changelog
Every line below is output from the schema gate that runs on each pull request.
That gate diffs the proposed schema against the one on main, classifies every
difference, and refuses the change if anything is breaking. This page is that
output, dated, with the tool's [log] prefix stripped and nothing else
touched.
Which means the changelog cannot drift from the API: a change nobody wrote down here still had to pass the gate, and the gate's report is the entry.
How to read an entry
| Mark | Classification | What it means for you |
|---|---|---|
| ✔ | Non-breaking | Purely additive. Existing documents keep working, unchanged |
| ⚠ | Dangerous | Legal, and worth reading: a new enum value a client may not handle, a new optional argument, a default that moved |
| ✖ | Breaking | Would invalidate an existing document. The gate refuses it. A line like this can only appear alongside a numbered, dated decision explaining why it was accepted anyway |
The ⚠ class deserves a moment. A new enum value is not a breaking change to
this API, but it can be one to your client — if you switch on
CampaignStatus and throw on the default branch, adding a status breaks you,
not us. Handle unknown enum values defensively; they will arrive.
How anything ever leaves
Nothing is removed in one step. Removal is: mark it deprecated with a reason
and a removal date, wait past that date, then remove. There is no
date-versioned API and no v2 endpoint — the graph is one living schema that
only grows, and deprecations are the pressure valve.
Deprecated coordinates are grouped at the foot of their page in the reference, so "what is on its way out" is always one page away from "what it is".
2026-08-26 — Ads: the merchant's own figures
The Ads module stopped reporting figures it computed and started reporting the ones each advertising platform reports. That is a bigger change than the list suggests: a platform is now a thing in the graph, it carries the month it reports and a readiness score broken into what moves it, every account and campaign carries the week-by-week series a chart draws, and the blended totals are folded over PLATFORMS rather than over campaign lifetimes. Nothing was removed and nothing broke; one field is on its way out, with a date.
⚠ Enum value GRAB_ADS was added to enum AdProvider
⚠ Enum value CONVERSIONS was added to enum CampaignObjective
⚠ Enum value MESSAGES was added to enum CampaignObjective
⚠ Enum value ORDERS was added to enum CampaignObjective
✔ Field adPlatforms was added to object type Query
✔ Type AdPlatform was added
✔ Type AdPlatformConnection was added
✔ Type AdPlatformEdge was added
✔ Type AdPlatformHealth was added
✔ Type AdMonthlyTotals was added
✔ Type AdWeeklyPoint was added
✔ Field platform was added to object type AdAccount
✔ Field brand was added to object type AdAccount
✔ Field countsTowardsBlended was added to object type AdAccount
✔ Field monthly was added to object type AdAccount
✔ Field weeklySeries was added to object type AdAccount
✔ Field attributedRevenue was added to object type Campaign
✔ Field countsTowardsBlended was added to object type Campaign
✔ Field format was added to object type Campaign
✔ Field returnOnAdSpend was added to object type Campaign
✔ Field weeklySeries was added to object type Campaign
✔ Field attributedRevenue was added to object type BlendedMetrics
✔ Field connectedSpend was added to object type BlendedMetrics
✔ Field healthScore was added to object type BlendedMetrics
✔ Field monthlyBudgetCap was added to object type BlendedMetrics
✔ Field returnOnAdSpend was added to object type BlendedMetrics
✔ Field totalCustomers was added to object type BlendedMetrics
✔ Field BlendedMetrics.totalConversions is deprecated
✔ Description on type AdAccount has changed
✔ Description on type AdAccountConnection has changed
✔ Description on type BlendedMetrics has changed
✔ Description for enum value AdAccountStatus.PAUSED changed
✔ Field BlendedMetrics.blendedCac description changed
✔ Field BlendedMetrics.totalConversions description changed
✔ Field BlendedMetrics.totalSpend description changed
✔ Field Campaign.conversions description changed
✔ Field Campaign.dailyBudget description changed
✔ Field Campaign.spendToDate description changed
✔ Field Query.adAccounts description changed
✔ Field Query.blendedMetrics description changed
✔ Field Query.campaigns description changed
Worth knowing, beyond the list:
- Four new enum values, and this is the release the ⚠ note above was written
for.
AdProvidergainsGRAB_ADS;CampaignObjectivegainsCONVERSIONS,MESSAGESandORDERS, because flattening a click-to-WhatsApp campaign intoSALESreports it as a shopping one. If youswitchon either and throw on the default branch, this release reaches you. totalConversionsis deprecated, and this is the first field with a removal date: 2027-01-31. Rename tototalCustomers. Identical value — the platforms report customers, andblendedCacdivides by that — so the migration is the spelling and nothing else.blendedCactruncates, and always did not. It is now stated: $980 over 227 customers is $4.3171…, reported as $4.31. Rounding would publish $4.32, a cost the merchant has not incurred.blendedMetricschanged BASIS. It used to sum every campaign's lifetime; it now sums the platforms' reported months. A platform reports a month, a campaign reports a window of weeks, and adding campaign totals up answered no question a merchant asks. It also could not express "outside the blend", which a channel the merchant is only trialling needs: those are reported alongside the totals and left out of them, so trialling a channel cannot move the number a lender reads. ReadcountsTowardsBlendedon a platform, an account or a campaign to see which side of that line it is on.returnOnAdSpendonBlendedMetricsis a mixed basis, deliberately. The numerator spans the whole blend; the denominator isconnectedSpendonly. Both denominators are published, so recompute on either if you prefer one.AdPlatform.returnOnAdSpendandCampaign.returnOnAdSpendare single-subject and mix nothing.AdAccountno longer claims one account per platform. It never really held — a Meta merchant typically has Facebook, Instagram and WhatsApp Business — and the description said otherwise. Do not key a collection of accounts byprovider. An account is also listed on a platform that has not been connected yet; readstatusandplatform { connected }.AdPlatformHealth's four components have four DIFFERENT maxima — tracking 3, bidding 1, budget efficiency 4, structure 2, adding to the tenscoreis reported out of. Render each against its own, never against the score.- A platform that is not connected still reports figures, and its health card is a projection of what connecting would find rather than a measurement. That is the point of the module — what a channel would cost before committing to it — but say so in your interface.
weeklySeriesis a plain list, not a connection. A reporting window is read as a unit, so it is not paged; it carries a hard ceiling instead, at most 53 points.Campaign.dailyBudgetof zero means "not set". The platforms this merchant uses cap spend on the account, not per campaign. Render it as not set rather than as $0.00, and readaccount { platform { monthlyBudgetCap } }for the ceiling that does apply.
2026-08-24 — Sales, and a pass over every description
The third module, and the first time the graph carried two services built on the
same stack — which is why a PageCursor gains a directive below rather than a
field. Everything else in the second block is wording: the schema said several
things about how it is built internally that a reader of it has no use for, and
that pass is separately worth reading if you generate types from descriptions.
✔ Field merchant was added to object type Query
✔ Field orders was added to object type Query
✔ Field order was added to object type Query
✔ Field products was added to object type Query
✔ Field product was added to object type Query
✔ Field salesSummary was added to object type Query
✔ Field adjustProductPrice was added to object type Mutation
✔ Type AdjustProductPriceInput was added
✔ Type AdjustProductPriceResult was added
✔ Type ChannelSummary was added
✔ Type DateRange was added
✔ Type DateRangeInput was added
✔ Type DateTime was added
✔ Type Merchant was added
✔ Type Order was added
✔ Type OrderConnection was added
✔ Type OrderEdge was added
✔ Type OrderLine was added
✔ Type OrderStatus was added
✔ Type Product was added
✔ Type ProductConnection was added
✔ Type ProductEdge was added
✔ Type ProductPriceAdjusted was added
✔ Type ProductPriceError was added
✔ Type ProductPriceErrorCode was added
✔ Type SalesChannel was added
✔ Type SalesSummary was added
⚠ Directive shareable was added to object PageCursor
✔ Description on type Query has changed
✔ Description on type Mutation has changed
✔ Description on type AdsPageInfo has changed
✔ Description on type Employee has changed
✔ Field Node.id description changed
✔ Field AdAccount.id description changed
✔ Field Campaign.id description changed
✔ Field Query.employees description changed
✔ Field Query.leaveRequests description changed
✔ Field Mutation.pauseCampaign description changed
Worth knowing, beyond the list:
salesSummaryis computed from the orders, not stored beside them. Ask it for a period and the revenue, order count, average order value and refund rate it returns reconcile with the orders in that period — including the per-channel breakdown. A period with no orders answers with zeroes, not an error.Moneyis the same type you already know from Ads, minor units and a currency, shared deliberately so one amount does not mean two things depending on which module returned it.SalesChannelis an enum, and it will grow. New selling surfaces arrive as new values — handle the unknown branch.adjustProductPricerefuses rather than no-ops. Setting the price it already has comes back asUNCHANGED_PRICEon the error branch, because an audit trail that records changes that did not happen is describing something other than what happened.PageCursoris now@shareable. Nothing about the type changed; the directive is what lets two services publish the same framework-generated type into one graph. No document is affected.- The description pass. Nine coordinates said something about subgraphs, supergraphs, transactions or paging strategy — true statements about the inside of this system, and of no use to anyone calling it. They now say what the field is. If your codegen embeds descriptions as doc comments, this is the release where they change.
2026-08-24 — Ads
The second module, and the first proof that two independently built services compose into one graph. Money, campaigns and the blended cost-per-acquisition figure the module exists for.
✔ Field adAccounts was added to object type Query
✔ Field campaigns was added to object type Query
✔ Field blendedMetrics was added to object type Query
✔ Field pauseCampaign was added to object type Mutation
✔ Type AdAccount was added
✔ Type AdAccountConnection was added
✔ Type AdAccountEdge was added
✔ Type AdAccountStatus was added
✔ Type AdProvider was added
✔ Type AdsPageInfo was added
✔ Type BlendedMetrics was added
✔ Type Campaign was added
✔ Type CampaignActionError was added
✔ Type CampaignActionErrorCode was added
✔ Type CampaignConnection was added
✔ Type CampaignEdge was added
✔ Type CampaignObjective was added
✔ Type CampaignPaused was added
✔ Type CampaignStatus was added
✔ Type CurrencyCode was added
✔ Type Money was added
✔ Type PauseCampaignInput was added
✔ Type PauseCampaignResult was added
Worth knowing, beyond the list:
Moneyis minor units only.amountMinor: Int!plus a currency, never a float, and never divided by 100 without checking the currency first — the rupiah, the yen and the dong have no minor unit at all.blendedCacis not an average of averages. It is total spend over total conversions, so a channel with ten conversions does not weigh the same as one with two hundred. That difference is the entire point of the field.AdsPageInfo, notPageInfo. Why.
2026-08-23 — People
The first module, and the schema's first shape: cursor connections, opaque global identifiers, and a mutation whose expected failures come back as data.
✔ Field node was added to object type Query
✔ Field nodes was added to object type Query
✔ Field employees was added to object type Query
✔ Field employee was added to object type Query
✔ Field leaveRequests was added to object type Query
✔ Field requestLeave was added to object type Mutation
✔ Type Employee was added
✔ Type EmployeeConnection was added
✔ Type EmployeeEdge was added
✔ Type EmployeeStatus was added
✔ Type EmploymentType was added
✔ Type LeaveRequest was added
✔ Type LeaveRequestConnection was added
✔ Type LeaveRequestCreated was added
✔ Type LeaveRequestEdge was added
✔ Type LeaveStatus was added
✔ Type LeaveType was added
✔ Type LeaveValidationError was added
✔ Type LeaveValidationErrorCode was added
✔ Type Node was added
✔ Type PageCursor was added
✔ Type PageInfo was added
✔ Type RequestLeaveInput was added
✔ Type RequestLeaveResult was added
✔ Type Date was added
✔ Type UUID was added
Worth knowing, beyond the list:
idis opaque and global. It encodes the type alongside the record's key, so it is unique across the whole graph and can be refetched throughnode. It is never a database key.requestLeavereturns a union. A rejected request isLeaveValidationErrorindata, not an entry inerrors[].
There is no earlier history to reconstruct. The first entry above is the first schema this API ever published; the gate has been running since the commit that created it.