API changelog
Every dated API version with its breaking changes, and the additive changes each contracts release brought.
A request names its version in the FurniOps-Version header. Without one, it gets the version
its organisation was pinned to on first use. Breaking changes ship only in a new dated version,
and an older version keeps its shape: responses and webhook events are rewritten for it. Additive
changes (new endpoints, fields, events and enum values) reach every version as they are released.
Versions
| Version | Status | Breaking changes |
|---|---|---|
2026-10-04 | latest | none |
2026-10-01 | supported | none |
Breaking changes by version
2026-10-04
No breaking changes.
2026-10-01
No breaking changes.
Additive changes by contracts release
0.103.0
- The organisation export gains the
assistant_notesdataset: the organisation's notes for FurniOps AI, asGET /v1/assistant/memories?scope=organswers them, written afterwebhook_deliveries. Every member reads them, so the dataset needs no read permission (null, likeorg). Members' own notes stay out of the organisation's archive.
0.102.0
- Billing for a live launch. A subscription shows a plan change held for the end of the term
(
scheduled_change), andcancelScheduledChangedrops it. The account carries who invoices are made out to (details), the credit balance a proration left and the overdue invoice of a past-due subscription.updateBillingDetailsedits the business name and billing email. The preview says when the plan takes effect (timing,effective_at), what is charged today (amount_due,credit_applied) and when it was quoted (quoted_at, whichsubscribetakes back to charge exactly the quote);subscribealso takes the billing email and business name. A declined card is the new402 payment_declined.
0.101.1
- The changelogs name the assistant FurniOps AI throughout and describe the catalogue consumer features without naming a customer. Text only; no shape changes.
- The change feed's cursor semantics, documented: a caught-up page's
cursoris the feed's horizon, so a page that matched nothing (a quietcataloguesfilter, atypesfilter) still moves forward and a reader that keeps reading never falls behind retention. The stream's heartbeat and end move the last event ID on to how far it read. Descriptions only; no shape changes.
0.101.0
- Mapping field
cartons(variant, repeatable) with a new value kindcartons, which fills the canonical variant'scartons: one cell lists a variant's shipping boxes, split on the column's separator, eachsize; weight; countin any order (49 x 18 x 7 in; 60 lb; 2 boxes). The column's unit is a length unit; a weight states its own;;cannot be a cartons separator (CARTONS_CELL_FORMAT).
0.100.0
- What a consumer that mirrors catalogues needs from FurniOps.
- Catalogue filter:
catalogues(comma-separatedcat_IDs, at most 50) onGET /v1/changes,GET /v1/changes/recentandGET /v1/stream, andfilters.catalogueson webhook endpoints: only events whosecatalogueis listed (a source health event's is the catalogue the source feeds). The feed cursor moves past the events it skips. Endpoint reads without the field parse as no filter. served_byon runs: which of a source's APIs served the run, named by its connector (Coasterv2, orv1after a fallback); null for a connector with one API.- Stored images: each
media[]of a product or variant gainsid(img_) and, once stored,stored_url, a stable API URL. The newgetMedia(GET /v1/media/{id},products:read) answers the stored WebP's size and a download link signed for 15 minutes. - Products list:
catalogueandsourcefilters onGET /v1/products. - Offer withdrawal: a source that stops stating an offer withdraws it after a complete
run: kept under its ID with
quantity0, noincoming,availabilityout_of_stock, prices as last stated, and the newwithdrawn_at(null or absent while stated);offer.changedsays so (facetsstockandavailability). Stating it again clearswithdrawn_at.OFFER_WITHDRAWALholds the values. - SDK
clientCredentials({ tokenUrl, clientId, clientSecret, scope? }): a refreshableapiKeyfor a machine client. One shared token, replaced once a fifth of its life is left (at least 30 s); one token request for concurrent callers; retries on 429, 5xx and lost connections with backoff andRetry-After;client_secret_basic(default) orclient_secret_post; the current token served while a refresh fails. When the API answers 401, the client drops the token and sends the call once more with a fresh one. AlsoClientCredentialsError,isRefreshableApiKeyandfurniops.media.get.
- Catalogue filter:
0.99.0
- The assistant is named FurniOps AI in every description, summary and catalogue
label. The MCP relay tool is
ask_furniops_ai(titled "Ask FurniOps AI"), its answer object isassistant_answer, and the relay's exports are renamed:ASSISTANT_MCP_TOOL,ASSISTANT_MCP_ANNOTATIONS,MCP_ASSISTANT_WAIT_SECONDS,MCP_ASSISTANT_REQUEST_STATE_SECONDS,assistantAskInputSchema,assistantAnswerSchema,ASSISTANT_ANSWER_STATUSESand their types. The relay was not served yet, so no client breaks.
0.98.0
-
FurniOps AI credit: an organisation on a paid plan buys credit (1 credit = 1¢ of list price, held as micro-USD) that Ask AI spends once the day's allowance is used.
- Eight public routes under Billing, all out of MCP and never an agent's tool:
getCreditBalance,listCreditEntries,getCreditRateCard,previewCreditPurchase,createCreditPurchase,listCreditPurchases,getCreditPurchaseandupdateCreditSettings. Buying and the settings needbilling:write(owner only, never an API key) and a person's own token. - Schemas for the balance, purchases (a one-off invoice paid in the Payment Element), ledger
entries, lots (12-month expiry) and the rate card (tokens per credit per model, worked out from
the dated prices). ID prefixes
crp,creandcrl. - Shared math:
listMicros(cost plus margin, rounded once),fundingSplit,creditHoldMicros,spendFromLots(earliest expiry first), money to credit and back, refund clawback per refund, purchase bounds per currency, lot expiry andpriceRiseDays(30 days' notice for a rise). - AI configuration:
margin_bpson dated prices; the platform's default margin (+30%), credit cap and purchase limits on the settings;list_microsandcredit_microson usage rows. - Ask AI: the
creditsrefusal reason,fundingon the member view, the organisation's daily credit limit on its settings, and a member's cap up to $200 (it may pass the allowance). - Events
credits.purchased,credits.low,credits.exhaustedandcredits.expiring(webhooks only), audit actions, email kinds, and the errorscurrency_unsupported,model_unpricedandprice_notice_too_short.
- Eight public routes under Billing, all out of MCP and never an agent's tool:
0.97.0
- FurniOps AI on paid plans: plans gain
assistant_daily_micros, the organisation's daily Ask AI spend under the plan (0 by default), and the org settings show it asplan_daily_micros, the ceiling of the organisation's own budget.
0.96.0
-
Contracts for the rest of FurniOps AI.
- Agent flags:
RouteSpec.agent: false(never a tool),people: true(people only) andagentAsks(always asks, onRouteSpecandInternalRouteSpec);AgentTool.agent_asks. - Support access: conversation
context(platform,support,tenant) besidesurface, withorg,acting_forandlivemode; theFurniOps-Support-Tokenheader;support_token_required,support_token_invalidandsupport_renewal_required; the token'ssupport_use; grants'useand a burst counted over distinct organisation and member pairs; request logs'via; auditon_behalf_ofandAuditDelegationData. - Ask AI: the public
/v1/assistant/*routes, member and organisation settings, spend, the plan featureassistant, the AI purposeassistant.dashboardand a tenant daily cap. No public route acts alone. - MCP, CLI and memory: surfaces
dashboard,mcpandcli; the MCP relay's ask input and answer; memory schemas and routes (mem_IDs,memory_limit,assistant_memories:write, never a key's), whose create and delete tools always ask and are never offered over MCP.
- Agent flags:
0.95.0
- Agent tools say whether an agent may run them unasked (
acts_alone). A route opts in withagentActsAlone, and only a plainwritethat previews and can be undone qualifies.
0.94.0
- Assistant approvals name the tool call they belong to (
tool_call_id), so a client can pair each approval with its tool part. Also exports theAssistantConversationDeletedtype.
0.92.0
- Exports gain the
organisationkind: everything the organisation holds, one gzip JSON Lines file per dataset (EXPORT_DATASETS, each line as its API read answers it), closed by a manifest. It needs every dataset's read permission (403insufficient_scope), takes no filters (422unprocessable), and is neither plan-gated nor metered. Every export file now names itsdataset. - The agent tool manifest:
buildAgentTools(routes, principal)lists every operation a principal may call as an agent's tool, with its risk, preview, undo, re-authentication and whether a person must do it;GET /v1/agent/tools(listAgentTools) serves it. The SDK gains the route. GET /v1/statusandGET /v1/status/history(public, kept out of MCP): the platform's measured health per status-page component, the latest level from the last 3 minutes and 90 days of daily rollups (minutes checked, degraded and failing).push_recordsgets a lean MCP tool. A route can name anmcpBody: the body its tool lists, when its own schema is too large for a client's tool list. The route still checks every call in full.pushRecordslists an outline ofRecordsInput(the mode, each product's key and common fields, its variants' and offers'), 3.7 KB in place of 43 KB, and every other field passes through. OpenAPI is unchanged.- The
identity.brand_claims:writescope reads as one plain sentence on a consent screen, without a parenthetical in the middle.
0.90.0
- Offers on their own:
GET /v1/offers(listOffers,offerListQuerySchema,offerListItemSchema) lists every source's price and stock the caller's licences show, newest first, filtered by variant, product, source, availability orupdated_since, each naming its variant and catalogue, and its product, title and SKU where the licences show them; the SDK gainsoffers.list. The webhook event catalogue (WEBHOOK_EVENT_CATALOG,WEBHOOK_EVENT_GROUPS) names every subscribable event type with its group and description, for event pickers.
0.89.0
- Vendor portal: a vendor publishes its catalogue to the network and can withdraw it
(
GET /v1/catalogue,POST /v1/catalogue/publish,POST /v1/catalogue/withdraw; publishing needs the data agreement and a verified vendor profile), hides single products on a plan withproduct_visibility(POST /v1/catalogue/visibility), and follows each change as a publication run (GET /v1/catalogue/publication-runs[/{id}]). Newpublishingpermission (vendors only; owners and admins write). Products that appear on or leave the network are announced asproduct.publishedandproduct.withdrawn. The own catalogue's variants with price levels (wholesale, MAP, MSRP), availability and issues (GET /v1/catalogue/variants) and its images (GET /v1/catalogue/images). Products are written through a push source withPOST /v1/sources/{id}/records, whole (upsert) or as patches (merge); sources say whether they arefirst_party. Vendor analytics gainavailable_to, a weekly series and the most read products, each figure from at least k distinct retailers.
0.88.0
- Claim your brand: the vendor directory (
GET /v1/vendor-profiles, any organisation) and a vendor's own brand (GET/PATCH /v1/brand), claims with graded evidence (DNS on the brand's domain, members' verified addresses, FurniOps invitations, trademark references) filed, checked and withdrawn under/v1/brand/claims, and invitation previews. A new vendor-onlybrandpermission whose writes stay with people, thevnd,vclandvciIDs, and thebrand_unverifiedproblem for publishing before a brand is verified. Retailers receivecatalogue.verifiedandcatalogue.unverified.
0.81.1
- The undo route's description: a retried undo answers 409 (the entry is undone already), and undoing the undo's own entry redoes the change.
0.81.0
- Undo through the audit log:
POST /v1/audit-events/{id}/undo(undoAuditEvent, permissionaudit:write, not idempotent itself, previewable),AuditEvent.undo(kind, available, why not, until when) andundo_of, theundo_unavailableproblem, andPOST /v1/match-pairs/{id}/reopen(reopenMatchPair), which is now what undoesdismissMatchPair.
0.78.0
- Previews and undo declarations. Every public write declares how it answers
FurniOps-Dry-Run(transactional,custom, ornonewith a reason) and how it is undone (inverse, a namedroute, ornonewith a reason);previewGaps(table)finds writes that do not. NewFurniOps-Dry-Runheader (true|explain; never ignored: a route that cannot preview answers 422preview_unsupported), thePreviewschema answered byexplainon the route's own status asapplication/vnd.furniops.preview+json, andx-furniops-preview/x-furniops-undoin the OpenAPI document.
0.77.0
- Billing launch contracts. The plan catalogue:
plans.ts(plan intervals, immutable plan prices, catalogue plans) and the publicGET /v1/plans. Plan definitions gainlive(defaults to true for older definitions; overrides never grant it by omission) and aplan_requiredproblem. Subscribing takes an interval, a promotion code, a billing address and a tax ID;POST /v1/billing/previewprices it with tax; the billing account reportslive, the grace window, the address and the tax ID; the subscription reports its interval,cancel_atand discount. New ID prefixesplp,cpn,pmc.
0.76.0
- AI configuration changes take an optional
reason(1–500 characters, trimmed), recorded in the platform audit log with the change.
0.74.0
- Routes can keep body fields from their MCP tool (
mcpOmit): the tool refuses them and its description says a person changes them.update_webhook_endpointno longer takesurl, so text an agent reads can never redirect an endpoint's events.
0.73.1
- MCP tools' output schemas have an object at the root (the root
$refis inlined), as MCP requires; 2025-era clients no longer see list and resource results wrapped in{ result }.
0.73.0
- MCP tools generated from the route contracts:
buildMcpTools(routes, sets)turns every GET route with an API-key permission and a JSON answer into a read tool. Write tools are the non-GET routes an API key may hold, never security routes. A tool's arguments are the route's path parameters and query, plusbody,idempotency_keyandif_matchwhere the route takes them. They are JSON Schema 2020-12, and the tools come sorted by name.mcpCallturns a tool call into the route's request. A route opts out withmcp: false.mcp/tools.jsonis generated and checked like the OpenAPI document.
0.72.0
- Billing routes:
GET /v1/billing,POST /v1/billing/subscription,POST /v1/billing/card,POST /v1/billing/subscription/canceland/resume,GET /v1/billing/invoices. Payments are confirmed with Stripe's Payment Element; Stripe's webhooks change the plan.
0.71.0
- The
request_logs:readpermission for the organisation's API request logs. Owners, admins and developers hold it, and an API key may too. Adds the routesGET /v1/request-logsandGET /v1/request-logs/{id}(RequestLog), with logs kept 31 days.
0.70.0
- The
oauth_appsresource:oauth_apps:readandoauth_apps:writefor the OAuth apps an organisation registers. Owners, admins and developers hold both. Write is human-only: no API key can mint client secrets. Adds theocsID prefix for an OAuth app's client secrets.
0.69.0
- Shadow record:
AgentSettings.shadowcounts an agent's shadow proposals of the last 30 days and how many a person later made by hand the same way (agreed) or otherwise (differed), the measure for promoting it.
0.68.0
- Triage notes: the
triageproposal kind, a plain-words explanation of a run's failure or problems from the triage agent. Each cause cites the run's error or one column's cells with one problem (TriageIssue) and says who can fix it (TRIAGE_FIXERS). There is nothing to apply: accepting one answers 409, reject dismisses it.TriageEvidencerecords what the note was checked against.
0.67.0
- Source probes and onboarding proposals:
POST /v1/source-probesqueues a polite probe of a vendor URL (robots.txt obeyed, at most 25 requests),GET /v1/source-probes/{id}andGET /v1/source-probesread it with its report (platform and evidence, walls, the public feed, sitemaps, JSON-LD coverage, an effort estimate). Thesourceproposal kind is the onboarding agent's proposed source from a probe (always high risk; accepting creates it), with its evidence.
0.66.0
- Mapping proposals from the repair agent: the
mappingproposal kind (columns for the headers a mapping left unplaced) with its evidence (rows proved on, problems before and after, fields gained), and theagentmapping tier for columns an agent's proposal applied without a person.
0.65.0
GET /v1/organswers the organisation's plan and the features it switches on (CurrentOrganisation); the audit timeline filters by time (since,until); an endpoint's deliveries filter by oneevent.
0.64.0
-
AI agents' proposals: the
proposalresource (prp_), with its payload and proof per kind (category rules first);GET /v1/proposals,GET /v1/proposals/{id},POST /v1/proposals/{id}/accept|reject; each agent's autonomy and daily budget (GET /v1/agents,PATCH /v1/agents/{agent}); webhook-onlyproposal.createdandproposal.appliedevents.The SDK gains the proposal and agent settings operations.
0.61.0
GET /v1/merges(listMerges): the organisation's merges, newest first, by status and product group, to find a merge to undo whoever decided it. A review pair carriesmerge, the merge a person made from it.
0.59.0
- Add
GET /v1/changes/recent(listRecentChanges): the change feed's events newest first, paged backwards withstarting_after, with a forwardcursoron the first page forGET /v1/changesorGET /v1/stream.
0.58.0
GET /v1/orgreads the caller's organisation (ID, name, slug), andGET /v1/me/orgslists every organisation the signed-in user belongs to with their role (user tokens only). NewOrganisationandMembershipschemas.
0.57.0
- Server-side product filters:
GET /v1/productstakesstatus,brand(ignoring case and spacing),category(that category or below it),availability(at least one offer of it) andq.qmatches products whose title or brand holds every word, or with a variant whose SKU or MPN is the text. Every filter given must match.brand,categoryandqread content fields, so they match only in catalogues whose content the organisation sees from every source.
0.56.2
- The
exportsresource description cites its decision instead of a plan step, so consent screens that strip decision refs show it in plain words. A test keeps plan references out of every resource description.
0.56.1
- Declares 503
unavailableon the routes that can answer it. Creating a source, starting or completing an upload, starting an export, reading the change feed or stream, merging, dismissing or undoing a match, and reading or changing the judge settings can each be refused while a dependency is missing, while the organisation's catalogue is still being set up, or while the instance's wait pool is full. Like a shed,unavailablealways carriesRetry-After.
0.56.0
- The public document lists every event pushed to webhook endpoints in OpenAPI
webhooks, each with its named schema, including the organisation's own (delivery.disabled,usage.threshold_reached,export.completed) andwebhook.test. Responses that name what they made or queued now document theirLocationheader. Reading usage no longer listsquota_exceeded, because it is never refused for a spent quota. The SDK'sWebhookEventnow covers every event webhooks deliver. The newChangeEventtype is what the change feed and stream carry.
0.55.0
-
FurniOps-Mode: live | testpicks the data a call acts on. The public spec declares it on every bearer-authenticated operation. An OAuth access token (a person, or an org's machine client) uses it to reach the organisation's sandbox, and is live when it is absent. An API key's mode is its own: the header may repeat it, and naming the other mode is refused with 400invalid_request.The SDK takes
modeon the client and per request. The CLI takes--mode live|test(orFURNIOPS_MODE), so a device login can work in test mode.
0.54.1
- The public OpenAPI document declares the headers its refusals carry, as reusable header components:
Retry-After,RateLimitandRateLimit-Policyon every 429 (rate_limited,quota_exceeded), andRetry-Afteron every 503 shed (overloaded).createExportstates the open-export limit as the API applies it: at most 3 pending or running exports per mode, test and live counted apart. The SDK's generated types carry the new response headers.
0.54.0
- Bulk exports:
POST /v1/exports(rate classexport,exports:writeplusproducts:read, the plan's bulk exports),GET /v1/exports/{id}with 15-minute download links for its gzip JSON Lines files,GET /v1/exports, theexport.completedevent, theexp_ID prefix and theexportspolicy resource (developer and analyst may export).
0.53.0
- Feature gates: creating or updating a webhook endpoint with a
fatpayload answers 403feature_unavailablewhen the plan lacks fat webhooks (an endpoint whose plan lost it is sentthin); the change feed and delivery logs keep the plan's feed and delivery days when fewer than the service's 30.
0.52.0
- Metered usage:
GET /v1/usageandGET /v1/usage/series(usage:read), the 429quota_exceedederror on every authenticated route, the publicusage.threshold_reachedevent (usageEvents),LEVEL_METERSandusageStatus.
0.51.0
- Rate classes
exportandstream(RATE_CLASSES,routeRateClass;/v1/streamisstream), rate tiermax, and the 503overloadederror, documented on every route but writes.
0.50.0
- Webhook endpoints carry
api_version: the version whose shape their events have, named in each delivery'sFurniOps-Versionheader. Optional on create (defaults to the request's version) and update.
0.49.0
- Dated API versions:
2026-10-04is the first dated version and the latest;2026-10-01stays served with the same shape. The version-change registry (VERSION_CHANGES, empty for now) holds each breaking change with transforms keyed by resourceobjectand by event type, which rewrite the latest shape back to the version before it;downgradeToVersionapplies the changes newer than a caller's version, newest first, to a response body or an event envelope, andassertVersionChangeskeeps the registry ordered and bound to served versions. The API changelog (docs/api-changelog.md) is generated from the registry and this changelog, and gated bygenerate:check.
0.48.0
- Test mode:
triggerTestEvent(POST /v1/test/events) emits a sample public event in the organisation's sandbox, and thetest_mode_requiredproblem (403) answers a live key there.
0.47.0
- Matching's review routes:
listMatchPairsandgetMatchPair(review pairs, most probable first, with evidence, conflicts and the AI judge's answer),mergeMatchPairanddismissMatchPair,getMergeandundoMerge, andgetJudgeSettings/updateJudgeSettings. New ID prefixesmpr,mrgandmjd.
0.46.0
- Merge events:
product.merged/product.unmergedandvariant.merged/variant.unmerged, public, one per aggregate version. A merge namesmerged_into(its statusmerged, no fields); an unmerge lists every field again and namesunmerged_from.
0.45.0
- Fat event payloads and category filters: change events carry an optional
object, the product, variant or offer as of the event's version (without its children); webhook endpoints takepayload: "thin" | "fat"and acategoriesfilter; the change feed and stream take apayloadquery parameter.
0.44.0
- Change feed long-poll and live stream:
GET /v1/changestakeswait(0 to 25 s);GET /v1/stream(streamChanges) serves the feed as server-sent events withstreamQuerySchema. A route's response may declarecontentType: "text/event-stream": it is documented as such, with no ETag or 304, and the ETag lint rule exempts it.
0.43.0
- Webhook deliveries:
GET /v1/webhook-endpoints/{id}/deliveries[/{delivery}](attempt log),POST …/deliveries/{delivery}/resend,POST …/recover(failed deliveries in a time range),POST …/test(webhook.test);coalesce_secondson endpoints;webhookEventsregistry withdelivery.disabled(subscribable) andwebhook.test, which only webhooks deliver.
0.42.0
- Webhook endpoints:
WebhookEndpoint(secrets by hint only), create/list/get/update/delete androtate-secretroutes; a secret's value appears once, inWebhookEndpointWithSecret.
0.41.0
- Change feed and products read API: licence field groups (
fieldGroupOf,grantsGroup),publicBackendEvents,GET /v1/changes(ChangePage),GET /v1/productsandGET /v1/products/{id}(Product,ProductVariant,ProductOffer).
0.40.0
- Freshness routes:
POST /v1/demandrecords the variants a tenant wants kept fresh (their sources are checked at their fastest until the demand lapses), andPOST /v1/variants/{id}/checkschecks one variant's offers now, joining a run under way or answering with one that just finished. Both need the newfreshness:writepermission, which developers hold.
0.39.0
- Sources:
schedule.adaptive(default true) lets FurniOps check a source sooner when it changes often, and the read-onlyfreshnessshows what it learned per run stream (interval_minutes,changes_per_day,observations,learned_at).freshnessdefaults to nothing learned, so source events published before it still parse.
0.38.0
- Mapping field
tags: a product-level, repeatable list that fills the canonical product'stags.
0.37.0
- A canonical
tagsfield on products; thecredentialsrun error code;waitingon runs (a rate limit or a retry, until when, the attempt); the inbound message log (GET /v1/sources/{id}/inbound-messages,emlIDs) andPOST /v1/sources/{id}/inbound-address/rotate;testSourcecovers API sources through their connector.
0.36.0
- Categories: the FurniOps taxonomy (
listCategories; ID prefixctg, each category naming its GS1 GPC class) and per-source category rules (listSourceCategories,saveCategoryRules), the most specific rule winning. A canonical product carries the FurniOpscategoryits source's rules file it under.
0.35.0
- Uploads and mappings: staged uploads (
createUpload,getUpload,completeUpload; ID prefixupl) and the mapping flow (listMappings,getMapping,confirmMapping; ID prefixmap). The canonical field list (MAPPING_FIELDS, with level, kind and repeatability), mapping tiers (built_in,learned,template,synonym,person) and the shared validatormappingProblems(headers, columns)move to contracts.
0.34.0
- First sources:
SourceConfig.warehouse,price_code(api) andinbound.senders(file),SourceSchedule.stock_interval_minuteswithsourceScheduleProblems,Source.inbound_address, run streams (RUN_STREAMS,Run.stream) and an optionalcreateRunbody (RunCreate), with abodyOptionalroute flag the OpenAPI document honours.
0.33.0
- Runs carry
problems: the first cells normalise could not use (sheet, line, column, field, kind and detail), up toRUN_PROBLEMS_KEPT.stats["normalise.problems"]still counts every one.
0.32.0
- Source health:
status_reasonon sources (the run error behinddegradedorblocked), and the public eventssource.degraded,source.recoveredandsource.blocked.
0.31.0
- Runs:
POST /v1/sources/{id}/replay(replayRun) queues a run that processes the source's current snapshot again without fetching, and the run error codeno_snapshot.
0.30.0
- Catalogue change events:
product.created/updated/discontinued,variant.created/updated,offer.created/changed, all public. Each carries the diff per claim field with the new value's provenance; an offer change is one event per version withfacets(price,stock,availability) for filtering. Newchangesschemas andofferFacetOf.
0.29.0
- Runs: the run object (triggers, statuses, stages, error codes),
POST /v1/sources/{id}/runs,GET /v1/sources/{id}/runs,GET /v1/runs/{id}. Afilesource may now have a URL to be fetched from.
0.28.0
- Sources say how their values read:
config.currency,config.decimalandconfig.role(supplier or retailer), withsourceReading(kind, config)filling the defaults (usd, a dot, and retailer for storefront kinds, supplier otherwise).
0.27.0
- Sources: the
Sourceresource, its create/update/credentials/test bodies,sourceConfigProblems()(a kind's config rules, shared by the console and the API), seven public routes under/v1/sources.
0.26.0
- The canonical model v1:
canonicalProductSchemawith variants, offers, dimensions, cartons, rugs, compliance and media; canonical integer units (toHundredthsInch,toHundredthsPound,measuredLength,measuredWeight); GTIN check digits; provenance methods, source kinds, lifecycle and availability vocabularies; ID prefixescat,lic,rec.
0.24.0
- Add the first public route,
listAuditEvents(GET /v1/audit-events,audit:read), withactionandactorfilters;openapi/public.jsonand the policy matrix regenerated.
0.22.0
- The
feature_unavailableandplan_limit_reachedproblems (403): an org's plan does not include a feature, or the org is at one of its plan's limits.
0.20.0
- Resource scopes:
RESOURCE_SCOPE_KINDS(source→src_),ResourceScope(<kind>:<id>),isResourceScope,resourceScopeSchema,parseResourceScopes(known vs unknown kinds from the feed),scopeSelectionandinScope;Principal.resourceScopes. Scopes narrow data, never routes. A membership'sresource_scopeson the feed is now bounded (500 × 200 characters).
0.18.0
- The
akrID prefix: review items identity opens when an API key shows suspicious activity.
0.17.0
- Problems
interaction_superseded(409: a newer authorization request replaced this one in the browser) andaccount_mismatch(409: the application asked for a different account than the one signed in), so a client tells them apart from a plainconflictwithout readingdetail.
0.16.0
- Single sign-on: the
ssoresource (sso:read,sso:write; owners and admins, human only, never API keys), thedomandssoID prefixes, thesso_requiredproblem (403).
0.11.0
-
OpenAPI for first-party APIs and one shared artifact gate.
- contracts:
OpenApiInfo.auth("apiKey"default, or"session", documenting the__Host-fo_sessioncookie);SESSION_COOKIEis exported as the single source of the name; every write route now lists the CSRF refusal (permission_denied, 403), public ones included.
- contracts:
0.9.0
- Add the
pkeyID prefix (passkeys) and thereauthentication_requirederror code (403), for identity's passkeys and sudo.
0.8.0
- Add the
sesID prefix (sessions) and theinvalid_credentialserror code (401), for identity's sign-in.
0.7.0
-
Routes a signed-in principal calls on itself.
- contracts:
RouteSpec.permissionmay be"authenticated"(any principal, no org permission;RoutePermission,isPolicyKeyword). OpenAPI documents the credential errors but no scopes or permission errors, and the matrix opens it to every role.Principal.roleis optional: a principal without an org context holds no org permission.
- contracts:
0.6.0
- Handlers bound to route contracts.
0.5.0
-
Policy skeleton and authorisation matrix.
- contracts: permissions (
<resource>:<read|write>), built-in role grants,Principalwith restricted credentials (intersection),effectivePermissionsandcan. Route contracts replacescopeswithpermission(or"public") plus ariskclass, checked at definition.buildPermissionMatrix/renderPermissionMatrix;policy/matrix.mdis generated besideopenapi/public.jsonby the renamedgenerate/generate:checkscripts.
- contracts: permissions (
0.4.0
- contracts: route contracts (
defineRoute,createRouteTable,isIdempotentRoute) andbuildOpenApiDocument(OpenAPI 3.1 via zod-openapi), which adds the shared headers (FurniOps-Version,Request-Id,Idempotency-Key, ETag/If-None-Match/304,If-Match), the conventional and domain problem responses, and thex-furniops-*extensions.publicRouteswithPUBLIC_API_INFO; the generatedopenapi/public.jsonis published as@furniops/contracts/openapi.json.
0.2.0
- contracts: event registry (
defineEvent,createEventRegistry), cursor pagination (listQuerySchema,listSchema,encodeCursor/decodeCursor,toPage), money in minor units (moneySchema, exactmoneyFromDecimal/moneyToDecimal), length and weight measurements with converters, documented envelope fields, and error codes aligned with the IETF Idempotency-Key draft (idempotency_key_invalid400,idempotency_request_in_progress409,idempotency_key_reused422;idempotency_conflictremoved) plus one default code per status viadefaultCodeForStatus.