# FurniOps > Normalised furniture vendor data: products, variants, offers and the events that change them, over a versioned REST API, a change feed, a live stream and signed webhooks. The API is at https://api.furniops.com (latest version 2026-10-04, sent as the FurniOps-Version header). Every page below, in one file: https://docs.furniops.com/llms-full.txt. ## AI agents - [AI agents](https://docs.furniops.com/agents): FurniOps' AI agents file categories, repair mappings, draft new sources and explain failed runs. They never change your catalogue themselves. What each agent does, autonomy from off to applying low-risk changes, daily budgets, and the shadow record. - [Proposals](https://docs.furniops.com/agents/proposals): Every change an AI agent wants to make, with the proof FurniOps ran on it. What a proposal holds, how it is proved and rated low or high risk, its statuses, accepting and rejecting it, and the webhooks that announce it. ## API reference - [API reference](https://docs.furniops.com/api-reference): Every FurniOps API operation, generated from the public OpenAPI document, with the conventions they share. - [List the caller's agent tools](https://docs.furniops.com/api-reference/agents/list-agent-tools): Every operation of this API the caller may call, as tools for an AI agent: name, input schema, risk class, how a write answers a preview (`FurniOps-Dry-Run`), how it is undone, and whether a person must do it or re-authenticate. Filtered by the caller's role, key scopes and organisation kind, as the API checks them; a key not made for an agent gets the reads only. Identity serves its own operations at the same path. - [List the agents' settings](https://docs.furniops.com/api-reference/agents/list-agent-settings): Each AI agent's autonomy and daily budget, and what it spent today. - [Update an agent's settings](https://docs.furniops.com/api-reference/agents/update-agent-settings): Changes the settings given; the rest stay. `autonomy: off` stops the agent; proposals it made stay as they are. - [List proposals](https://docs.furniops.com/api-reference/agents/list-proposals): The AI agents' proposals for the organisation, newest first, each with the proof it passed. Those waiting for a person by default. - [Retrieve a proposal](https://docs.furniops.com/api-reference/agents/get-proposal): One proposal: what it would change, its proof, and who decided it. - [Accept a proposal](https://docs.furniops.com/api-reference/agents/accept-proposal): Applies the proposal as the person accepting it, through the same change a person makes (category rules: saved, then the source replayed; a mapping: confirmed, then replayed; a source: created), and needs that change's permission too (`sources:write`). A proposal not waiting for a person answers 409; one that no longer applies (the source paused or busy) answers 409 and stays proposed. A triage note has nothing to apply: accepting one answers 409; reject dismisses it. - [Reject a proposal](https://docs.furniops.com/api-reference/agents/reject-proposal): Records that the change should not be made. The agent does not propose the same change about the same thing again. A proposal not waiting for a person answers 409. - [List the organisation's acceptances](https://docs.furniops.com/api-reference/vendor/list-agreement-acceptances): Every agreement version the organisation accepted, newest first, with who signed and when: its record of what it agreed to. - [Read an agreement and where the organisation stands](https://docs.furniops.com/api-reference/vendor/get-agreement): The newest published version of the agreement, with its full text and hash, the organisation's newest acceptance, and its standing: `accepted`, `grace` (an earlier acceptance counts until `accept_by`), `required` or `unpublished`. Vendors only: a retailer answers 403 `permission_denied`. - [Accept an agreement for the organisation](https://docs.furniops.com/api-reference/vendor/accept-agreement): Records that the signer accepts, for the organisation, exactly the version and text they were shown (its `version` and `sha256`), with their typed name and title and their statement that they may bind it. A version or hash that is not the newest answers 409 `conflict`: show the new text and ask again. Accepting a version already accepted returns the earlier acceptance. Owners and admins only; never an AI agent's tool. - [Read the vendor's reach and data health](https://docs.furniops.com/api-reference/vendor/get-vendor-stats): How many paying retailers carry the vendor's products and how often they read them in the last 30 days, with the catalogue's completeness and freshness. Aggregates only, computed by a platform job: a count below its threshold is withheld, and no retailer is ever named. - [Read the vendor's most read products](https://docs.furniops.com/api-reference/vendor/get-vendor-product-stats): The vendor's products most read by retailers in the last 30 days, listing only those read by at least k distinct paying retailers, so no single retailer's reading shows. - [Read the vendor's reach week by week](https://docs.furniops.com/api-reference/vendor/get-vendor-stats-series): Retailers' reads of the vendor's products and the distinct retailers reading them, for each of the last 12 complete weeks. Each week is thresholded on its own; there is no daily series and no breakdown by retailer. - [List your conversations](https://docs.furniops.com/api-reference/assistant/list-assistant-conversations-for-me): Your conversations with FurniOps AI in this organisation and mode, latest first. Only your own: nobody else, admins included, reads them. - [Start a conversation](https://docs.furniops.com/api-reference/assistant/create-assistant-conversation-for-me): Starts an empty conversation in this organisation and mode, which it keeps for good; its first turn names it when no title is given. - [Retrieve a conversation](https://docs.furniops.com/api-reference/assistant/get-assistant-conversation-for-me): A conversation's messages, oldest first, and the approvals waiting for you. Only your own: nobody else, admins included, reads them. - [Delete a conversation](https://docs.furniops.com/api-reference/assistant/delete-assistant-conversation-for-me): Deletes the conversation, its messages and its approvals; pending approvals are dropped. What FurniOps AI changed stays changed, in the audit log. A turn still running answers 409. - [Talk to FurniOps AI](https://docs.furniops.com/api-reference/assistant/create-assistant-turn-for-me): Sends your next message, or your decisions on the pending approvals (every one of them), and streams FurniOps AI's answer as the AI SDK UI message stream. It acts as you, with your own permissions: reads run, and every change waits for your approval with its preview. Security operations are never run; it says what to do. One turn at a time per conversation (409), at most 20 steps, 25 tool calls and 120 seconds, in the conversation's mode. Refused when Ask AI is not in the plan (403 `feature_unavailable`), is off for the organisation or for you, or your role may not use it (403 `permission_denied`), no model is routed or AI is switched off (503), the month's `ai_tokens` are used up and no credit may be spent (429 `quota_exceeded`), or today's allowance is spent with no FurniOps AI credit to spend, or your personal budget is (429 `ai_budget_reached`). Model calls are paid from today's allowance first, then from the organisation's FurniOps AI credit when it is switched on (D-289). Changes are recorded in the audit log as Ask AI for you, naming the conversation. - [Retrieve Ask AI for you](https://docs.furniops.com/api-reference/assistant/get-my-assistant): Whether FurniOps AI can run for you here and why not, your switch, autonomy and memory, your daily budget and today's spend. - [Update Ask AI for you](https://docs.furniops.com/api-reference/assistant/update-my-assistant): Turns FurniOps AI off or on for you, sets your autonomy (at most the organisation's `autonomy_max`, else 422) or whether your memories load. Takes effect from your next turn. - [List your memories](https://docs.furniops.com/api-reference/assistant/list-assistant-memories-for-me): The notes FurniOps AI reads at the start of every turn here, oldest first: yours in this organisation, then the organisation's, each with where it came from and when it expires. - [Save a memory](https://docs.furniops.com/api-reference/assistant/create-assistant-memory-for-me): Saves a note FurniOps AI reads at the start of every turn: plain words, one paragraph, at most 400 characters, no links, addresses, Markdown or code. `scope: org` shares it with every member's FurniOps AI and needs `assistant_memories:write` (403 otherwise). At most 25 per person and 25 per organisation (409 `memory_limit`). Proposed by FurniOps AI, it waits for you to approve the exact words. Expires 365 days after its last edit. - [Delete all your memories](https://docs.furniops.com/api-reference/assistant/delete-assistant-memories-for-me): Deletes every memory of yours in this organisation for good (the organisation's stay), and answers how many went. - [Update a memory](https://docs.furniops.com/api-reference/assistant/update-assistant-memory-for-me): Rewrites a memory, or keeps it another 365 days (`renew`). An organisation's memory needs `assistant_memories:write`. - [Delete a memory](https://docs.furniops.com/api-reference/assistant/delete-assistant-memory-for-me): Deletes a memory for good: the audit log records that it went, never its words. An organisation's memory needs `assistant_memories:write`. - [Retrieve Ask AI for the organisation](https://docs.furniops.com/api-reference/assistant/get-assistant-org-settings): Whether the plan includes Ask AI and the organisation turned it on (who, when, which disclosure), the roles allowed, the most autonomy, the daily budgets and today's spend, MCP and memory switches, and who processes the data and how long it is kept. - [Update Ask AI for the organisation](https://docs.furniops.com/api-reference/assistant/update-assistant-org-settings): Turns Ask AI on (naming the data-handling disclosure accepted in `accept_disclosure`) or off, and sets the roles allowed, the most autonomy, the daily budgets (a member's at most the organisation's plus its daily FurniOps AI credit limit) and the MCP and memory switches. Not in the plan answers 403 `feature_unavailable`. Recorded in the audit log (`assistant_settings.updated`). - [Retrieve Ask AI spend](https://docs.furniops.com/api-reference/assistant/get-assistant-spend): The organisation's Ask AI spend today and this month, and per member: spend, turns and conversations kept. Never what anyone asked. - [List audit events](https://docs.furniops.com/api-reference/audit/list-audit-events): The organisation's audit timeline, newest first: what members, API keys, connected apps and FurniOps staff did, from sign-in policy to API keys and data operations. Filter by action, actor or time (`since`, `until`). Addresses are shown as a /24 or /48 prefix only. FurniOps AI acting for a member is the actor `agent` `assistant` with `on_behalf_of` the member. FurniOps staff acting through support access are the actor `staff`, with the member in `data.acting_for` and the grant in `data.support_grant`; `data.agent.conversation_id` names the assistant conversation when FurniOps AI made the change. - [Undo an audited change](https://docs.furniops.com/api-reference/audit/undo-audit-event): Puts back the change an audit entry records, by running the operation that undoes it as you: besides `audit:write`, you need the permission and scopes that operation needs, and the undo is recorded with `undo_of`. An entry's `undo` says whether it can be undone. A target changed since, or an entry already undone, answers 409; a change that cannot be undone, or older than 30 days, answers 422. So an undo never runs twice: a retry answers 409. To redo the change, undo the undo's own entry. An entry identity recorded (`source: identity`) answers 422 here: undo it with identity's `POST /v1/org/audit-events/{id}/undo`. With `FurniOps-Dry-Run` the undo is previewed. - [Read billing](https://docs.furniops.com/api-reference/billing/get-billing): The organisation's plan, subscription and card, and the plans on sale with their prices. `enabled` is false until selling starts. - [Change the card](https://docs.furniops.com/api-reference/billing/set-up-card): Answers a card setup the browser confirms with Stripe's Payment Element; once Stripe confirms it, the card is the one renewals are charged to. While billing is switched off, it answers 409 `conflict`. - [Read FurniOps AI credit](https://docs.furniops.com/api-reference/billing/get-credit-balance): The organisation's FurniOps AI credit in US dollars (micro-USD): the balance, what running Ask AI turns hold, what expires next, today's allowance and spend, how credit is spent, and whether you may buy more and within which amounts. Today's allowance pays first; credit pays once it is used, when `credits_after_allowance` is on. - [List credit entries](https://docs.furniops.com/api-reference/billing/list-credit-entries): The credit ledger, newest first: purchases, staff grants, Ask AI spend (per member, never what they asked), refunds, disputes and expiry, each with the balance after it. Filter by `kind` and time. - [List credit purchases](https://docs.furniops.com/api-reference/billing/list-credit-purchases): The organisation's credit purchases, newest first, with their invoices (receipts) and what was refunded. - [Buy FurniOps AI credit](https://docs.furniops.com/api-reference/billing/create-credit-purchase): Starts a purchase of the amount, in the billing currency and whole units, as a one-off invoice with tax, and answers the payment the browser confirms with Stripe's Payment Element; then poll `getCreditPurchase`. Credit is added only once Stripe reports the invoice paid, and expires 12 months later. The same `Idempotency-Key` answers the same purchase. Owner only, on a paid plan with Ask AI (403 `feature_unavailable`): refused while a renewal is past due, a payment is disputed or the organisation is being deleted (409), in a currency credit is not sold in (422 `currency_unsupported`), or outside the amounts and balance limit `getCreditBalance` states (422). While billing is switched off, it answers 409 `conflict`. - [Retrieve a credit purchase](https://docs.furniops.com/api-reference/billing/get-credit-purchase): One purchase. After the browser confirms the payment, poll it until `status` is `succeeded` (the credit is in) or `failed` (pay again with the same purchase). - [Preview a credit purchase](https://docs.furniops.com/api-reference/billing/preview-credit-purchase): What buying the amount would cost now, with tax from Stripe Tax and the credit it adds. Changes nothing. Owner only. While billing is switched off, it answers 409 `conflict`. - [Read the credit rate card](https://docs.furniops.com/api-reference/billing/get-credit-rate-card): What each model Ask AI uses costs at list price today, and how many of its tokens one credit (1¢) and one dollar buy, worked out from the dated prices; with the rates already set for later (a rise is announced 30 days ahead). Every member may read it. - [Update how credit is spent](https://docs.furniops.com/api-reference/billing/update-credit-settings): Whether Ask AI spends credit once today's allowance is used, the most credit spent per day, and the balance below which `credits.low` goes out. Owner only. Recorded in the audit log (`credits.settings_updated`). - [Update the billing name and email](https://docs.furniops.com/api-reference/billing/update-billing-details): The business name invoices are made out to and the email invoices and receipts go to. Saved on the Stripe customer at once. While billing is switched off, it answers 409 `conflict`. - [List invoices](https://docs.furniops.com/api-reference/billing/list-invoices): The organisation's invoices, newest first, with links to Stripe's hosted page and PDF. Empty while billing is switched off or before the first subscription. - [Preview a subscription](https://docs.furniops.com/api-reference/billing/preview-subscription): What subscribing to, or moving to, the plan and interval would cost now: fee, discount, tax and total, calculated by Stripe Tax from the address (without one, before tax), when it takes effect and what is charged today. An invalid promotion code is reported in `promotion`, not refused. Changes nothing. While billing is switched off, it answers 409 `conflict`. - [Subscribe, or change plan](https://docs.furniops.com/api-reference/billing/subscribe): Without a subscription, starts one on the plan and interval, with tax from the address and the promotion code's discount, renewing on the 1st (UTC) with the first, partial period prorated; usage past what the plan includes is billed monthly. It answers the payment the browser confirms with Stripe's Payment Element. With one, moves it to the plan with proration; a payment due now is confirmed the same way, and the plan changes only once it succeeds. A move to a lower tier (even monthly to annual), or annual to monthly on the same plan, waits for the end of the term (`subscription.scheduled_change`) and charges nothing today. The plan itself changes when Stripe confirms, within seconds. While billing is switched off, it answers 409 `conflict`. - [Cancel at the period's end](https://docs.furniops.com/api-reference/billing/cancel-subscription): The subscription ends when the fee's term does (the year, for an annual plan); the plan stays until then and returns to the free plan after, leaving live mode. Resume it any time before. While billing is switched off, it answers 409 `conflict`. Without a subscription, 409. - [Keep a cancelled subscription](https://docs.furniops.com/api-reference/billing/resume-subscription): Undoes a cancellation before the period ends. While billing is switched off, it answers 409 `conflict`. Without a subscription, 409. - [Cancel a scheduled plan change](https://docs.furniops.com/api-reference/billing/cancel-scheduled-change): Drops the change waiting for the end of the term (`subscription.scheduled_change`); the subscription goes on as it is. While billing is switched off, it answers 409 `conflict`. Without a scheduled change, 409. - [List the plans on sale](https://docs.furniops.com/api-reference/billing/list-plans): The listed, active plans in order, with what each grants and its monthly and annual prices; Enterprise has none (contact sales). Public and cached: the pricing page renders from it. Retailers' plans unless `kind=vendor`. - [Read the organisation's brand](https://docs.furniops.com/api-reference/brand/get-brand): The vendor profile the organisation holds, whether it can still be edited (only while unverified), and its pending claim. Every vendor organisation holds exactly one. Vendors only. - [Edit the organisation's brand](https://docs.furniops.com/api-reference/brand/update-brand): Changes the name, website domain or brand names of the organisation's own profile while it is unverified. A verified profile answers 409: FurniOps changes it on request, since its verification rests on that name and domain. - [List the organisation's claims](https://docs.furniops.com/api-reference/brand/list-brand-claims): Every claim the organisation filed, newest first, with its evidence and outcome. - [Claim a brand](https://docs.furniops.com/api-reference/brand/file-brand-claim): Asks FurniOps to let the organisation speak for a brand: a listed profile (`profile`), whose catalogue then moves to the organisation without copying anything, or its own profile, which then becomes verified. FurniOps gathers the evidence on the profile's website domain at once: a domain the organisation verified by DNS (strongest) and members whose verified address is at the domain. An invitation from FurniOps and a trademark registration add to it. Staff decide every claim, after re-checking the evidence. One claim may be pending at a time (409 otherwise), and a listed profile can be claimed only while the organisation's own catalogue is empty (409). Owners and admins only; never an API key or an AI agent. - [Read a claim](https://docs.furniops.com/api-reference/brand/get-brand-claim): One of the organisation's claims, with its evidence and, once decided, the outcome. - [Gather a claim's evidence again](https://docs.furniops.com/api-reference/brand/check-brand-claim): Re-reads the evidence of a pending claim now, checking the domain's DNS record again: use it after publishing the record or adding a member at the domain. A claim that is not pending answers 409. - [Withdraw a claim](https://docs.furniops.com/api-reference/brand/withdraw-brand-claim): Withdraws a pending claim. A claim that is not pending answers 409. - [Read an invitation to claim a brand](https://docs.furniops.com/api-reference/brand/preview-claim-invitation): Reads the invitation behind a code FurniOps mailed: the profile, the address invited, when it expires, and whether the signed-in member can accept it (their verified address must be the one invited). A code that is unknown, used, revoked or expired answers 404. The code travels in the body so it never lands in a URL log. - [Read the organisation's own catalogue](https://docs.furniops.com/api-reference/catalogue/get-catalogue): The catalogue the organisation owns in the request's mode, with its product counts. A vendor's catalogue also says where its publication stands and, in `blocked_by`, why publishing would be refused now. - [List the catalogue's images](https://docs.furniops.com/api-reference/catalogue/list-catalogue-images): Every image the own catalogue's records name, newest first, with what FurniOps did with it: stored (with a short-lived preview link), pending, or failed and why. - [List publication runs](https://docs.furniops.com/api-reference/catalogue/list-publication-runs): The catalogue's publication runs, newest first: its publishing history. - [Retrieve a publication run](https://docs.furniops.com/api-reference/catalogue/get-publication-run): One publication run and its progress. - [Publish the catalogue to the network](https://docs.furniops.com/api-reference/catalogue/publish-catalogue): Starts a run that makes every product of the vendor's catalogue that is not hidden visible to the network of paying retailers, each announced as `product.published`. Needs the data agreement (409 `agreement_required`) and a verified vendor profile (409 `brand_unverified`). A catalogue already published, a run under way, or a test-mode sandbox answers 409; a retailer's catalogue answers 403. - [List the catalogue's variants with price levels and stock](https://docs.furniops.com/api-reference/catalogue/list-catalogue-variants): The own catalogue's variants by ID, each with its price levels (wholesale, MAP, MSRP), its availability and what looks wrong with them, narrowed by every filter given. The network's rows are never listed here. - [Hide products from the network, or list them again](https://docs.furniops.com/api-reference/catalogue/set-product-visibility): Starts a run that hides the products named (each announced as `product.withdrawn` while the catalogue is published) or lists them again (`product.published`). A plan without `product_visibility` answers 403 `feature_unavailable`; a product not in the catalogue answers 422; a run under way answers 409. - [Withdraw the catalogue from the network](https://docs.furniops.com/api-reference/catalogue/withdraw-catalogue): Starts a run that takes every product of the vendor's catalogue back from the network, each announced as `product.withdrawn` so retailers drop it. Its data is kept and it can be published again. A catalogue that is not published, or a run under way, answers 409. - [List the FurniOps taxonomy](https://docs.furniops.com/api-reference/categories/list-categories): Every category of the FurniOps taxonomy in one list, parents before their children and siblings in display order. Each names the GS1 GPC class it falls under. - [List a source's categories](https://docs.furniops.com/api-reference/categories/list-source-categories): Every category path the source's current products name, and every path above them, in path order: how many products sit at or below each, the rule set there, the rule inherited from above, or a same-name suggestion. Products under a path with no rule have no category. - [Set a source's category rules](https://docs.furniops.com/api-reference/categories/save-category-rules): Sets or removes the rules given; other rules stay as they are. A rule covers its path and every path below it until a more specific rule. The source's current snapshot is replayed to apply them. An unknown category answers 422; a source with an active run, or a paused or disabled one, answers 409, and then nothing is saved. - [Read the change feed](https://docs.furniops.com/api-reference/changes/list-changes): The organisation's public events in commit order: catalogue changes in its own and its licensed catalogues (fields its licences do not cover are left out, and an event with nothing left is skipped) and its sources' health. Pass the page's `cursor` back to read on; `caught_up` says nothing committed is left. With `wait`, a read that finds nothing waits for the next event (long-poll). Events are kept 30 days (fewer when the plan's feed days are fewer): an older cursor answers 400, and the reader starts again from `GET /v1/products`. Too many waiting reads answer 429 (the organisation's) or 503 (the service's), with `Retry-After`. - [Read the newest changes first](https://docs.furniops.com/api-reference/changes/list-recent-changes): The change feed's events newest first, for a recent-changes view: the same events `GET /v1/changes` answers, as the caller's licences let it see them, in reverse commit order. Pass `next_cursor` back as `starting_after` for older ones; a page can hold fewer than `limit` events and still have more. The first page's `cursor` reads on forwards with `GET /v1/changes` or `GET /v1/stream` from just after its newest event. Only events the plan still keeps are listed. - [Stream the change feed](https://docs.furniops.com/api-reference/changes/stream-changes): The change feed as server-sent events (`text/event-stream`), for dashboards and consoles: each event's `event` is its type, its `data` the event as `GET /v1/changes` shows it, and its `id` a feed cursor. A reconnecting client sends the last `id` as `Last-Event-ID` and carries on after it. Without a cursor the stream starts with events committed from now on. A comment line is sent every 15 s while it is quiet. The server ends a stream after a while (the client reconnects, and its access is checked again). Webhooks or the feed, not the stream, are for systems that must not miss an event. - [List connections](https://docs.furniops.com/api-reference/connections/list-connections): The organisation's connections to vendor catalogues, newest first. - [Request a connection](https://docs.furniops.com/api-reference/connections/request-connection): Asks FurniOps to connect the organisation to a published vendor catalogue; staff decide and the organisation is mailed. A pending or approved connection answers 409 `connection_exists`; too many pending or today 409 `connection_limit`; a catalogue that declined or revoked the organisation recently 409 `connection_cooldown` (see `can_request_at`). A catalogue not published answers 404. Live mode only: a test-mode call answers 403 `permission_denied`. - [Read a connection](https://docs.furniops.com/api-reference/connections/get-connection): One connection with its history. Staff notes and staff names are never shown. - [Read a connection's assortment](https://docs.furniops.com/api-reference/connections/get-connection-assortment): Whether the whole connected catalogue flows, or the selected products only. - [Choose a connection's assortment](https://docs.furniops.com/api-reference/connections/update-connection-assortment): Sets the mode (`all` or `selected`) and the rules that select products: matching products join as `rule` items, now and as the vendor publishes new ones. Feed, stream, exports and webhook endpoints read with `scope=assortment` follow it; `scope=connected` still reads every connected product. Products added emit `assortment.added`. - [List assortment items](https://docs.furniops.com/api-reference/connections/list-assortment-items): The products selected from a connected catalogue, newest first. - [Add products to an assortment](https://docs.furniops.com/api-reference/connections/add-assortment-items): Selects up to 1,000 products of the connected catalogue at once (`manual` items). Each added product emits `assortment.added` so an integration loads it once; its history is not replayed. Products outside the catalogue or hidden are listed in `refused`. - [Remove products from an assortment](https://docs.furniops.com/api-reference/connections/remove-assortment-items): Unselects up to 1,000 products; each emits `assortment.removed`. A rule that still matches a product adds it back unless an `exclude` rule keeps it out. - [End a connection](https://docs.furniops.com/api-reference/connections/disconnect-connection): Ends an approved or suspended connection at once (`revoked`, reason `retailer_request`): the catalogue leaves the feed, stream and webhooks from the next statement. Copies already taken are the organisation's. Requesting again later needs FurniOps' approval. - [Withdraw a request](https://docs.furniops.com/api-reference/connections/withdraw-connection): Takes back a request FurniOps has not decided. Any other state answers 409. - [List connectors](https://docs.furniops.com/api-reference/sources/list-connectors): Every connector an `api` source can use: the streams it reads, the credentials to set with `PUT /v1/sources/{id}/credentials` and the source config it reads. One page, by name. - [List source probes](https://docs.furniops.com/api-reference/sources/list-source-probes): The caller's mode's probes, newest first, with their reports. - [Probe a vendor URL](https://docs.furniops.com/api-reference/sources/create-source-probe): Queues a probe of the URL: robots.txt obeyed, one request at a time, at most 25. Follow it with `GET /v1/source-probes/{id}`. With AI agents in the plan, the onboarding agent then proposes a source from the report when FurniOps can run one for it; a person decides. A catalogue may have 3 open probes and start 20 in a day; one more answers 409 `conflict`. - [Retrieve a source probe](https://docs.furniops.com/api-reference/sources/get-source-probe): A probe and, once it has succeeded, its report. - [List sources](https://docs.furniops.com/api-reference/sources/list-sources): The organisation's sources, newest first, paginated with cursors. - [Create a source](https://docs.furniops.com/api-reference/sources/create-source): Adds a vendor connection to the organisation's catalogue. The kind is fixed; `config` must fit it (a feed needs a URL and a platform, an API source a connector). Set credentials separately with `PUT /v1/sources/{id}/credentials`. - [Retrieve a source](https://docs.furniops.com/api-reference/sources/get-source): One source of the organisation. Its credentials are never returned. - [Update a source](https://docs.furniops.com/api-reference/sources/update-source): Changes a source's name, connector, config, schedule, status or licence basis. Send the ETag of the source you read as `If-Match`; a stale one is refused. - [Remove a source's credentials](https://docs.furniops.com/api-reference/sources/delete-source-credentials): Forgets the source's credentials; runs that need them fail until new ones are set. - [Set a source's credentials](https://docs.furniops.com/api-reference/sources/set-source-credentials): Replaces the source's credentials. They are sealed under the source's ID as they arrive and never returned; the response shows only `has_credentials`. - [Rotate a source's inbound address](https://docs.furniops.com/api-reference/sources/rotate-inbound-address): Gives the source a new inbound address at once; mail to the old one is refused from then on. For an address that leaked. Sources without `config.inbound` answer 422. - [List a source's inbound emails](https://docs.furniops.com/api-reference/sources/list-inbound-messages): What arrived at the source's inbound address, newest first, paginated with cursors: who sent it, what FurniOps did with it and why, and the run each attachment started. - [Test a source](https://docs.furniops.com/api-reference/sources/test-source): Reaches the source once, as a run would, and reports what it answered; nothing is stored. A source fetched from a URL is asked for it; an API source's connector signs in with the stored credentials. Kinds that are not fetched (files) answer 422. - [Record demand](https://docs.furniops.com/api-reference/freshness/record-demand): Tells FurniOps which variants matter now (sold, in carts, on backorder). The sources offering them are checked at their fastest, every 15 minutes, until the demand lapses; stock-only runs where the connector reads stock apart. Variants the organisation cannot see are listed as `unknown` and ignored. Sources with `schedule.adaptive: false` keep their intervals. - [Check a variant now](https://docs.furniops.com/api-reference/freshness/check-variant): Checks the variant's offers now, at a moment that matters such as checkout: each source offering it gets a run (stock only where the connector reads stock apart), joins one under way, or answers with one that finished moments ago (a minute for stock, 15 minutes for a whole catalogue). New values arrive as `offer.changed` events. 404 when the organisation cannot see the variant. - [List exports](https://docs.furniops.com/api-reference/exports/list-exports): The caller's mode's exports, newest first, paginated with cursors. Files are listed by `GET /v1/exports/{id}` only. - [Start an export](https://docs.furniops.com/api-reference/exports/create-export): Queues an export in the caller's mode, written as gzip JSON Lines. `products`: the catalogue the caller can see, one product per line as `GET /v1/products` returns it; needs `products:read` beside `exports:write` and the plan's bulk exports (403 `feature_unavailable`), and exported products count against the `export_rows` quota (429 `quota_exceeded`). `organisation`: everything the organisation holds, one file per dataset, each line as its API read returns it, closed by a manifest; needs every dataset's read permission (403 `insufficient_scope`), takes no filters (422), and is neither plan-gated nor metered. Follow it with `GET /v1/exports/{id}` or the `export.completed` event. An organisation may have at most 3 open (`pending` or `running`) exports per mode, test and live counted apart; one more answers 409 `conflict`. - [Get an export](https://docs.furniops.com/api-reference/exports/get-export): An export and, once it has succeeded, its files: each with a download link valid 15 minutes from this read. Read it again for fresh links; the files are kept 7 days. - [List holds](https://docs.furniops.com/api-reference/review/list-holds): Field values validation held back for a person, newest first: open ones by default, or those kept or released. Filter by source, rule or product. - [Retrieve a hold](https://docs.furniops.com/api-reference/review/get-hold): One held field value: what the source says, why it was held, and any decision. - [Keep a hold](https://docs.furniops.com/api-reference/review/keep-hold): Keeps the value FurniOps accepted before and stops asking while the source keeps sending the held value; nothing is replayed. A new value from the source opens the hold again. A hold that is not open answers 409. - [Release a hold](https://docs.furniops.com/api-reference/review/release-hold): Accepts the held value: a replay of the source applies it to the master record, and a price's baseline moves to it, so the same value is not held again. A new value from the source opens the hold again. A hold that is not open, or a source with a run under way, answers 409; staff may not decide a hold on a vendor's own source (403). - [Reopen a hold](https://docs.furniops.com/api-reference/review/reopen-hold): Undoes a release or a keep: the hold is open again, and after a release a replay of the source puts back the value FurniOps had accepted before it. An open hold answers 409, as does undoing a release while the source has a run under way. - [Retrieve a product's edits](https://docs.furniops.com/api-reference/review/get-product-edits): The fields people set by hand on the product and its variants: each is an operator claim that beats every source's value until it is cleared (provenance shows it as the winner). - [Set or clear fields by hand](https://docs.furniops.com/api-reference/review/update-product-edits): Sets fields of the product or of its variants to a value (`null`: the product has none), or clears earlier edits so the sources' values stand again. Edits are the catalogue's edits source's operator claims: they beat every source's value, are announced in the change feed like any change, and are audited. Identity fields, images and offers are not edited (400). A merged product's edits go on the product it was merged into. Undone by sending back the edits it replaced. - [Retrieve a product's provenance](https://docs.furniops.com/api-reference/review/get-product-provenance): Every source's current claim on each field of the product and its variants, the winner, and the survivorship rule that made it win. Prices and stock are offers (`listOffers`). - [List quarantined records](https://docs.furniops.com/api-reference/review/list-quarantined-records): Source records held back whole, newest first, with why. A record lifts on the next clean run; its field holds are in `listHolds`. - [Retrieve a mapping](https://docs.furniops.com/api-reference/mappings/get-mapping): One mapping: its headers, sample rows, mapped columns and what it could not place. - [Confirm a mapping](https://docs.furniops.com/api-reference/mappings/confirm-mapping): Confirms the mapping, as proposed or with the columns given, and applies it: a file waiting in `needs_mapping` resumes, or the source's current snapshot is replayed. Confirming again with new columns changes it the same way. Columns that are not among the headers, a field mapped twice, or settings that do not fit the field answer 422; a source with an active run, or a paused or disabled one, answers 409. - [List a source's mappings](https://docs.furniops.com/api-reference/mappings/list-mappings): The source's mappings, one per header set its files have had, newest first. A run that stopped as `needs_mapping` waits for its `proposed` one. - [List review pairs](https://docs.furniops.com/api-reference/matching/list-match-pairs): Pairs of variants in the organisation's catalogue that may be one item, most probable first: the evidence for and against each, its decisive conflicts and the AI judge's answer. Open pairs by default. - [Retrieve a review pair](https://docs.furniops.com/api-reference/matching/get-match-pair): One pair: its variants, evidence, conflicts and the AI judge's answer. - [Dismiss a review pair](https://docs.furniops.com/api-reference/matching/dismiss-match-pair): Records that the two are different items: the pair is not offered again, however they are scored later. A pair that is not open answers 409. - [Merge a review pair](https://docs.furniops.com/api-reference/matching/merge-match-pair): Joins the two variants as one item, and their product groups as one product (the older survives). Undo it with the merge's undo. A pair that is not open, or whose records changed so they can no longer merge, answers 409. - [Reopen a dismissed review pair](https://docs.furniops.com/api-reference/matching/reopen-match-pair): Puts a dismissed pair back in the review queue, as it was before it was dismissed: it can be merged or dismissed again. A pair that is not dismissed answers 409. - [Retrieve the AI judge's settings](https://docs.furniops.com/api-reference/matching/get-judge-settings): Whether the AI judge looks at the organisation's review pairs, its daily budget and how much of it was used, the confidence at which it merges, and the model it asks. - [Update the AI judge's settings](https://docs.furniops.com/api-reference/matching/update-judge-settings): Changes the settings given; the rest stay. `auto_merge_confidence: null` sends every answer to people; `model: null` returns to FurniOps' default. - [List merges](https://docs.furniops.com/api-reference/matching/list-merges): The organisation's merges, newest first, whoever decided them: matching's rules, its scores, the AI judge or a person. Filter by status or by a product group on either side, to find a merge to undo. - [Retrieve a merge](https://docs.furniops.com/api-reference/matching/get-merge): One merge: the product groups and variants it joined, who decided it, and its undoing. - [Undo a merge](https://docs.furniops.com/api-reference/matching/undo-merge): Splits what the merge joined, each product and variant back to what its own sources say. Matching offers the pair again for review, never merges it again unseen. A merge already undone, or one a later merge built on, answers 409. - [List the user's organisations](https://docs.furniops.com/api-reference/organisation/list-my-orgs): Every organisation the signed-in user belongs to, with their role in it, by name; one page (at most 100). A user's token only: an API key, a machine client or a support session answers 403 `permission_denied`. Switching organisation means a new token for it. - [Read the caller's organisation](https://docs.furniops.com/api-reference/organisation/get-org): The organisation the credential belongs to: its ID, name and slug, and its plan with the features it switches on. A rename or a plan change reaches it within seconds. - [Get a stored image](https://docs.furniops.com/api-reference/products/get-media): FurniOps' stored copy of an image a product or variant names (its `media[].id`; `media[].stored_url` points here): the copy's size and a download link signed for 15 minutes, so a reader can keep its own copy without fetching from the vendor. Readable when the organisation can see a product or variant that names the image, or, as any retailer browsing the network may, a shared product or variant of a published vendor catalogue names it (`GET /v1/network/products`), else 404: an image only a hidden or unpublished product names stays out of reach. Until the copy is stored, `status` is `pending` or `failed` and `download_url` is null. - [List offers](https://docs.furniops.com/api-reference/products/list-offers): Every source's price and stock the organisation can see, newest first: filter by variant, product, source, availability or `updated_since`. Each offer shows only the fields its licences cover, as in `GET /v1/products`, and names its variant and catalogue; its product, title and SKU only where the licences show them. - [List products](https://docs.furniops.com/api-reference/products/list-products): Products the organisation can see, by ID. With `updated_since`, only those that changed since then (a variant or offer change counts): how a feed reader catches up after its cursor expired. `status`, `brand`, `category`, `availability` and `q` narrow the list further; every filter given must match. - [Get a product](https://docs.furniops.com/api-reference/products/get-product): A master product with its variants and offers, from the organisation's own or a licensed catalogue, with only the fields its licences cover. - [Browse vendor products](https://docs.furniops.com/api-reference/network/list-network-products): Published vendor catalogues' products, as any retailer browses them before connecting: facts, images, sizes and the list price (MSRP). Never stock, offers or trade price levels: connect to the vendor for those, and for the change feed and webhooks. Each image names FurniOps' stored copy (`media[].id`, and `stored_url` once stored), which `GET /v1/media/{id}` answers to a browsing organisation too. Hidden products and catalogues not published are left out. Not exportable. - [Read a vendor product](https://docs.furniops.com/api-reference/network/get-network-product): One published vendor product as browsing shows it, with the caller's connection to its catalogue. A product that is hidden or in a catalogue not published answers 404. - [List request logs](https://docs.furniops.com/api-reference/request-logs/list-request-logs): The organisation's API requests in the request's mode (live or test), newest first, kept 31 days: who called what and the answer. Filter by status class, method, actor, path prefix or time. A request appears within seconds; reading the logs is not logged. - [Get a request log](https://docs.furniops.com/api-reference/request-logs/get-request-log): One request, by its `Request-Id` (the newest, if a client reused one). - [Retrieve a run](https://docs.furniops.com/api-reference/runs/get-run): One run: its stage, status, counters and, when it failed, why. - [Replay a source](https://docs.furniops.com/api-reference/runs/replay-run): Queues a run that processes the source's current snapshot again without fetching, after a mapping or rule change: only records whose result changed are written, at the time they were fetched, and absence is not counted. It answers 409 like starting a run, and 422 when the source has no snapshot its records reflect yet. - [List a source's runs](https://docs.furniops.com/api-reference/runs/list-runs): The source's runs, newest first, paginated with cursors. - [Start a run](https://docs.furniops.com/api-reference/runs/create-run): Queues a pull of the source now, outside its schedule. A source has one active run at a time: while one is queued or running, or while the source is paused or disabled, this answers 409. Sources whose kind cannot be fetched yet, and a `stock` pull of a source whose connector does not read stock apart, answer 422. The body is optional. - [Write products to a push source](https://docs.furniops.com/api-reference/records/push-records): Sends up to 1000 products (at most 5 MB) to a `push` source of the organisation's own catalogue and queues the run that reads them, like a file. `upsert` replaces each product's record whole; `merge` changes only the fields given, keeping the rest. A push never discontinues products it does not name. Another kind of source answers 422; a source with an active run, or a paused or disabled one, answers 409; a vendor without the current data agreement answers 409 `agreement_required` once its catalogue is published. - [Start an upload](https://docs.furniops.com/api-reference/uploads/create-upload): Stages a file for a `file` source: the response's `target` is a presigned URL to PUT the bytes to, with the headers to send, for 15 minutes. Then complete the upload. Other kinds of source, and a file larger than the snapshot limit, answer 422. - [Retrieve an upload](https://docs.furniops.com/api-reference/uploads/get-upload): One upload: its status and, once completed, the run reading it. No `target`. - [Complete an upload](https://docs.furniops.com/api-reference/uploads/complete-upload): Checks that the bytes arrived, at the declared size, and queues a run that reads them. Missing or different bytes answer 422; an expired or already completed upload, a source with an active run, or a paused or disabled one answer 409. - [Read the platform's current health](https://docs.furniops.com/api-reference/status/get-status): Each component's latest measured level, from the last 3 minutes. Public and cached for a minute; incidents are posted on status.furniops.com. - [Read the platform's daily health](https://docs.furniops.com/api-reference/status/get-status-history): The last 90 days per component, in UTC days: minutes measured, slow and failing. Public and cached for 5 minutes. - [Trigger a sample event](https://docs.furniops.com/api-reference/test-mode/trigger-test-event): Emits a sample event of the type in the organisation's sandbox, with `livemode: false`, like any change: to the test-mode webhook endpoints that want it, and onto the sandbox's change feed and stream. Its objects are samples that do not exist. Test keys only: a live key answers 403 `test_mode_required`. - [Read the month's usage](https://docs.furniops.com/api-reference/usage/get-usage): Every meter's usage in a calendar month (UTC), in the caller's mode, against its quota. Counts add up over the month; a level (products tracked) is its highest reading. Quotas cap live mode only: at 80% the organisation receives `usage.threshold_reached`, and at 100% the metered action answers 429 `quota_exceeded` until the month ends. API calls are counted as they happen and may lag by a few seconds. - [Read one meter over time](https://docs.furniops.com/api-reference/usage/get-usage-series): One meter by hour (up to 7 days) or by day (up to 400 days), in the caller's mode; periods without usage are left out. A level meter's point is its highest reading in the period. - [Search the vendor directory](https://docs.furniops.com/api-reference/vendor-directory/list-vendor-profiles): Brands on FurniOps, by name: every vendor profile with its website domain, whether the brand verified it, and its catalogue's ID (the `catalogue` products carry). Each also shows whether its catalogue is published to the network, how many shared products it holds and its top categories (counted every few minutes, `facts_at`), and the calling organisation's own connection to it (`connection`, read on every call, with `can_request_at` after a decline or revocation). Search by part of a name (`q`, spelling-tolerant), by website domain, or narrow to published catalogues (`published=true`). No profile names the organisation that holds it. - [Read a vendor profile](https://docs.furniops.com/api-reference/vendor-directory/get-vendor-profile): One brand on FurniOps: its name, brands, website domain and verification, its catalogue's public facts, and the calling organisation's own connection to it. - [List webhook endpoints](https://docs.furniops.com/api-reference/webhooks/list-webhook-endpoints): The organisation's endpoints, newest first. - [Create a webhook endpoint](https://docs.furniops.com/api-reference/webhooks/create-webhook-endpoint): Starts pushing the chosen public events to the URL, signed with Standard Webhooks `v1` (`webhook-id`, `webhook-timestamp`, `webhook-signature`). The answer carries the signing secret once. Live-mode endpoints need HTTPS; URLs that resolve to private addresses are refused (422). An organisation has at most 20 endpoints. - [Get a webhook endpoint](https://docs.furniops.com/api-reference/webhooks/get-webhook-endpoint): One endpoint; its secrets appear only by their last characters. - [Update a webhook endpoint](https://docs.furniops.com/api-reference/webhooks/update-webhook-endpoint): Changes the URL, description, event types, filters or status. Send the endpoint's ETag as `If-Match`. Setting `enabled` resumes an endpoint FurniOps disabled; events missed while it was disabled are not sent (read them from the change feed). - [Delete a webhook endpoint](https://docs.furniops.com/api-reference/webhooks/delete-webhook-endpoint): Stops every delivery to the endpoint and forgets its secrets. - [List an endpoint's deliveries](https://docs.furniops.com/api-reference/webhooks/list-webhook-deliveries): The events sent (or being sent) to the endpoint, newest first, kept 30 days (fewer when the plan's delivery days are fewer). Filter by `status` (`failed` shows what to recover), event `type`, or one `event`. - [Get a delivery](https://docs.furniops.com/api-reference/webhooks/get-webhook-delivery): One delivery with the payload as sent and every attempt: when, the status, how long it took, and the start of the receiver's answer. - [Send a delivery again](https://docs.furniops.com/api-reference/webhooks/resend-webhook-delivery): Queues a finished delivery (failed or succeeded) to be sent again at once, with the same `webhook-id` (receivers that already have it may ignore it) and a fresh schedule. A delivery still pending or being sent is answered as it is. The endpoint must be enabled (409). - [Send failed deliveries again](https://docs.furniops.com/api-reference/webhooks/recover-webhook-deliveries): Queues every failed delivery created in the range to be sent again, oldest first, each with a fresh schedule; order per subject is kept. Use it once a receiver is fixed. Events never stored for the endpoint (it was disabled) are not sent: read them from the change feed. - [Rotate an endpoint's secret](https://docs.furniops.com/api-reference/webhooks/rotate-webhook-secret): Makes a new signing secret, shown once in the answer. The current secret keeps signing beside it for `overlap_hours` (default 24), so deliveries carry both signatures until the receiver has switched. A secret already rotated out is retired at once. - [Send a test event](https://docs.furniops.com/api-reference/webhooks/send-webhook-test): Sends a `webhook.test` event to the endpoint now, signed like any other, whatever it subscribes to. Follow it with `GET /v1/webhook-endpoints/{id}/deliveries/{delivery}`. - [assortment.added](https://docs.furniops.com/api-reference/webhook-events/assortment-added): Products joined a connection's assortment, chosen or by a rule: load them once (their history is not replayed). Signed with the endpoint's secret (Standard Webhooks `v1`). - [assortment.removed](https://docs.furniops.com/api-reference/webhook-events/assortment-removed): Products left a connection's assortment: they no longer flow with `scope=assortment`. Signed with the endpoint's secret (Standard Webhooks `v1`). - [catalogue.unverified](https://docs.furniops.com/api-reference/webhook-events/catalogue-unverified): A vendor catalogue is no longer verified by its brand: FurniOps took an approved claim back. Signed with the endpoint's secret (Standard Webhooks `v1`). - [catalogue.verified](https://docs.furniops.com/api-reference/webhook-events/catalogue-verified): A vendor catalogue is now verified by its brand: FurniOps approved the brand's claim. Signed with the endpoint's secret (Standard Webhooks `v1`). - [change_batch](https://docs.furniops.com/api-reference/webhook-events/change-batch): Many changes of connected vendor catalogues in one POST, for an endpoint in `batch` delivery (D-332). Fixed at its first attempt: a retry resends the same body under the same `webhook-id`. Signed with the endpoint's secret (Standard Webhooks `v1`). - [connection.approved](https://docs.furniops.com/api-reference/webhook-events/connection-approved): FurniOps approved the organisation's connection to a vendor catalogue: read its products (a snapshot, or an export with a catalogue filter), then the change feed from `baseline`. Signed with the endpoint's secret (Standard Webhooks `v1`). - [connection.declined](https://docs.furniops.com/api-reference/webhook-events/connection-declined): FurniOps declined the organisation's request to connect to a vendor catalogue, with a reason code; it may ask again after the cooldown. Signed with the endpoint's secret (Standard Webhooks `v1`). - [connection.reinstated](https://docs.furniops.com/api-reference/webhook-events/connection-reinstated): FurniOps lifted a suspension: re-read the catalogue (a snapshot) and the change feed from the new `baseline`, since changes made while suspended were not sent. Signed with the endpoint's secret (Standard Webhooks `v1`). - [connection.revoked](https://docs.furniops.com/api-reference/webhook-events/connection-revoked): A connection ended, by FurniOps or by the organisation. Copies already taken are the organisation's: apply its own policy to them (zero stock, draft or delete). Signed with the endpoint's secret (Standard Webhooks `v1`). - [connection.suspended](https://docs.furniops.com/api-reference/webhook-events/connection-suspended): FurniOps paused a connection: the catalogue's data stops flowing at once. No per-product withdrawals are sent. Signed with the endpoint's secret (Standard Webhooks `v1`). - [credits.exhausted](https://docs.furniops.com/api-reference/webhook-events/credits-exhausted): Available credit reached zero: once today's allowance is spent too, Ask AI answers 429 `ai_budget_reached`. Signed with the endpoint's secret (Standard Webhooks `v1`). - [credits.expiring](https://docs.furniops.com/api-reference/webhook-events/credits-expiring): A lot of credit expires in 30 days with credit left in it, once per lot. Signed with the endpoint's secret (Standard Webhooks `v1`). - [credits.low](https://docs.furniops.com/api-reference/webhook-events/credits-low): Available credit fell below the organisation's `low_balance_micros`, once each time it crosses. Signed with the endpoint's secret (Standard Webhooks `v1`). - [credits.purchased](https://docs.furniops.com/api-reference/webhook-events/credits-purchased): Stripe reported a credit purchase's invoice paid and its credit was added: once per purchase. Signed with the endpoint's secret (Standard Webhooks `v1`). - [delivery.disabled](https://docs.furniops.com/api-reference/webhook-events/delivery-disabled): FurniOps disabled a webhook endpoint: no delivery succeeded for 72 hours, or it answered 410 Gone (`status_reason`). Sent to the organisation's other endpoints that subscribe to it. Signed with the endpoint's secret (Standard Webhooks `v1`). - [export.completed](https://docs.furniops.com/api-reference/webhook-events/export-completed): An export succeeded or failed. Read it (`GET /v1/exports/{id}`) for its files, or its error. Signed with the endpoint's secret (Standard Webhooks `v1`). - [offer.changed](https://docs.furniops.com/api-reference/webhook-events/offer-changed): A source's offer changed (price, stock or availability; see `facets`): the changed fields. A source that stops stating an offer withdraws it: `withdrawn_at` is set, `quantity` becomes 0, `incoming` empty and `availability` `out_of_stock` (facets `stock` and `availability`); stating it again clears `withdrawn_at`. Signed with the endpoint's secret (Standard Webhooks `v1`). - [offer.created](https://docs.furniops.com/api-reference/webhook-events/offer-created): A source offered a variant at a location for the first time: every field. Signed with the endpoint's secret (Standard Webhooks `v1`). - [product.created](https://docs.furniops.com/api-reference/webhook-events/product-created): A master product was made from a source record: every field, with `old: null`. Signed with the endpoint's secret (Standard Webhooks `v1`). - [product.discontinued](https://docs.furniops.com/api-reference/webhook-events/product-discontinued): A master product was discontinued: its sources stopped listing it for long enough (the absence ladder). Signed with the endpoint's secret (Standard Webhooks `v1`). - [product.merged](https://docs.furniops.com/api-reference/webhook-events/product-merged): A master product was merged into another (matching or a person): it now redirects to `merged_into`, and reads of its ID answer with the survivor. Signed with the endpoint's secret (Standard Webhooks `v1`). - [product.published](https://docs.furniops.com/api-reference/webhook-events/product-published): A vendor's product became visible to the network (its catalogue was published, or the product listed again): every field, with `old: null`, like `product.created`. Its variants and offers follow as their own `.created` events. Signed with the endpoint's secret (Standard Webhooks `v1`). - [product.unmerged](https://docs.furniops.com/api-reference/webhook-events/product-unmerged): A merge was undone: the product is its own again under its original ID, with every field. Signed with the endpoint's secret (Standard Webhooks `v1`). - [product.updated](https://docs.furniops.com/api-reference/webhook-events/product-updated): A master product's data or lifecycle status changed (other than to discontinued): the changed fields. Signed with the endpoint's secret (Standard Webhooks `v1`). - [product.withdrawn](https://docs.furniops.com/api-reference/webhook-events/product-withdrawn): A vendor's product left the network (its catalogue was withdrawn, or the product hidden): no fields. Drop it, its variants and its offers; a later `product.published` brings it back. Signed with the endpoint's secret (Standard Webhooks `v1`). - [proposal.applied](https://docs.furniops.com/api-reference/webhook-events/proposal-applied): A proposal was applied: accepted by a person, or at once as low risk under `auto_low_risk`. Signed with the endpoint's secret (Standard Webhooks `v1`). - [proposal.created](https://docs.furniops.com/api-reference/webhook-events/proposal-created): An AI agent proposed a change that passed its proof and waits for a person: accept or reject it (`POST /v1/proposals/{id}/accept`). Signed with the endpoint's secret (Standard Webhooks `v1`). - [source.blocked](https://docs.furniops.com/api-reference/webhook-events/source-blocked): A source kept failing (or its URL is refused) and is now `blocked`: no runs until the organisation sets it `active`. `status_reason` says why. Signed with the endpoint's secret (Standard Webhooks `v1`). - [source.degraded](https://docs.furniops.com/api-reference/webhook-events/source-degraded): A source failed several runs in a row and is now `degraded`; it is still scheduled. `status_reason` says why. Signed with the endpoint's secret (Standard Webhooks `v1`). - [source.recovered](https://docs.furniops.com/api-reference/webhook-events/source-recovered): A degraded source ran successfully and is `active` again. Signed with the endpoint's secret (Standard Webhooks `v1`). - [usage.threshold_reached](https://docs.furniops.com/api-reference/webhook-events/usage-threshold-reached): A meter reached 80% or 100% of its monthly quota in live mode, once a month per meter and threshold. At 100% the metered action is refused (429 `quota_exceeded`) until the month ends. Signed with the endpoint's secret (Standard Webhooks `v1`). - [variant.created](https://docs.furniops.com/api-reference/webhook-events/variant-created): A master variant was made from a source record: every field, with `old: null`. Signed with the endpoint's secret (Standard Webhooks `v1`). - [variant.merged](https://docs.furniops.com/api-reference/webhook-events/variant-merged): A master variant was merged into another (the same item from two sources): it now redirects to `merged_into`, and its offers moved there. Signed with the endpoint's secret (Standard Webhooks `v1`). - [variant.unmerged](https://docs.furniops.com/api-reference/webhook-events/variant-unmerged): A variant merge was undone: the variant is its own again under its original ID, with every field and its offers back. Signed with the endpoint's secret (Standard Webhooks `v1`). - [variant.updated](https://docs.furniops.com/api-reference/webhook-events/variant-updated): A master variant's data changed: the changed fields. Signed with the endpoint's secret (Standard Webhooks `v1`). - [webhook.test](https://docs.furniops.com/api-reference/webhook-events/webhook-test): A test event, sent only when asked for (`POST /v1/webhook-endpoints/{id}/test`), whatever the endpoint subscribes to. Signed with the endpoint's secret (Standard Webhooks `v1`). ## Authentication - [API keys](https://docs.furniops.com/authentication/api-keys): Secret and restricted keys, the key wizard, every scope a key can hold, templates, access policies, expiry, rotation, keys for AI agents, suspicious-activity reviews and what to do when a key leaks. - [Authentication](https://docs.furniops.com/authentication): The credentials FurniOps accepts, which one to use for what, how the mode of a request is decided, and what every credential can never do. - [OAuth apps](https://docs.furniops.com/authentication/oauth-apps): Register an OAuth app for your organisation, sign members in with the authorization code flow and PKCE, call the API as the organisation with client credentials, and handle scopes, consent, token lifetimes, refresh rotation and secret rotation. ## Reference - [API changelog](https://docs.furniops.com/changelog): Every dated API version with its breaking changes, and the additive changes each contracts release brought. - [MCP server](https://docs.furniops.com/mcp): Connect Claude, ChatGPT, Cursor or any MCP client to FurniOps. How to sign in, which tools you get, how writes stay safe, and the limits. ## CLI - [Command reference](https://docs.furniops.com/cli/commands): Every furniops command, argument and flag in CLI 0.2. - [CLI](https://docs.furniops.com/cli): The furniops command line tool. Install it, sign in, use profiles, and script it with JSON output and exit codes. - [furniops listen](https://docs.furniops.com/cli/listen): Receive real FurniOps events on your laptop, signed exactly as production deliveries are, without a tunnel. ## Concepts - [The change feed](https://docs.furniops.com/concepts/change-feed): The ordered, replayable log of every change you can see, read with a cursor, long-polled with wait, or streamed live over server-sent events. - [Freshness](https://docs.furniops.com/concepts/freshness): How FurniOps decides how often to check each source, how you tell it what matters now, and how to ask for a fresh stock check. - [Catalogues and licences](https://docs.furniops.com/concepts/licences): Your own catalogue, the network catalogue, and how a licence narrows what you can see of it by source and by field group, the same way on every path. - [Master records and provenance](https://docs.furniops.com/concepts/master-record): How many vendors' descriptions of a product become one master record, what each field remembers about its origin, and why IDs never change. - [Plans and features](https://docs.furniops.com/concepts/plans-and-features): What your organisation's plan decides, what feature_unavailable means, how fat webhooks fall back to thin, and how long the change feed and delivery log reach back. - [Live and test](https://docs.furniops.com/concepts/test-and-live): Every organisation has live data and a sandbox. What belongs to one mode, what the two share, and how a request's mode is decided. - [API versions](https://docs.furniops.com/concepts/versions): Dated API versions, the pin your organisation gets on its first request, the FurniOps-Version header, and how webhook endpoints choose the shape of their events. ## Get started - [Your first webhook](https://docs.furniops.com/get-started/first-webhook): Create a webhook endpoint, verify its signatures with the SDK, and test it on your machine with furniops listen. - [Overview](https://docs.furniops.com/get-started/overview): What FurniOps is, how its data reaches you, and the pieces you build with. - [Quickstart](https://docs.furniops.com/get-started/quickstart): Get an API key, make your first request with curl, then do the same with the TypeScript SDK. - [Test mode](https://docs.furniops.com/get-started/test-mode): Build against a sandbox catalogue with test keys, a seeded sample catalogue and sample events, without touching live data. ## Guides - [Bulk exports](https://docs.furniops.com/guides/bulk-exports): Export your whole catalogue, or everything changed since a time, as gzip JSON Lines files, then download and decompress them. - [ETags and concurrent changes](https://docs.furniops.com/guides/concurrency): Cache reads with If-None-Match, and stop two writers overwriting each other with If-Match. - [Errors](https://docs.furniops.com/guides/errors): The problem details every error answers with, every error code and what to do about it, and which errors to retry. - [Events reference](https://docs.furniops.com/guides/events): Every event FurniOps publishes, generated from the API's own contract. What each one means, which channels deliver it (webhooks, the change feed, the stream), whether test mode can emit a sample, and its payload. - [Idempotent requests](https://docs.furniops.com/guides/idempotency): Retry any POST safely with an Idempotency-Key. What is stored, for how long, and what each answer means. - [Pagination](https://docs.furniops.com/guides/pagination): Cursor pagination on every list, in both directions, and auto-pagination in the SDK. - [React to price and stock changes](https://docs.furniops.com/guides/price-changes): Use offer.changed to update prices and stock within seconds, tell price moves from stock moves, and check stock at the moment that matters. - [Rate limits](https://docs.furniops.com/guides/rate-limits): Budgets by rate class and tier, the IETF RateLimit headers that state them, what a 429 and a 503 overloaded mean, and how to back off. - [Request logs](https://docs.furniops.com/guides/request-logs): Every API request your organisation made in the last 31 days, with its answer and the credential behind it. Find a failing request by its ID, filter by status, method, caller, path and time, in the dashboard or the API. - [Sync a catalogue](https://docs.furniops.com/guides/sync-catalogue): Keep a complete, correct copy of your FurniOps catalogue in your own database, with a backfill, the change feed, and webhooks for speed. - [Sync a connected catalogue](https://docs.furniops.com/guides/sync-connected-catalogue): Keep a copy of a connected vendor's catalogue with connections.sync(), scope what flows with your assortment, and handle every state a connection can be in. - [Usage and quotas](https://docs.furniops.com/guides/usage-and-quotas): What FurniOps meters, how a month is counted, reading usage with GET /v1/usage, the 80% and 100% threshold events, and what a spent quota refuses. - [Handle webhooks securely](https://docs.furniops.com/guides/webhook-security): Verify Standard Webhooks signatures with or without the SDK, refuse replays, rotate secrets without downtime, and answer in a way FurniOps understands. - [Webhook endpoints and deliveries](https://docs.furniops.com/guides/webhooks): The full reference for webhooks. Endpoints and their modes, event types and filters, thin and fat payloads, burst windows, versions, limits, the delivery log, the retry schedule, automatic disabling, recovery, resending, secrets and the dashboard's controls. - [The Workbench](https://docs.furniops.com/guides/workbench): The dashboard's developer drawer. Send real API requests with your session in test or live mode, copy them as curl, keep this tab's history, and watch webhook health, without creating a key. ## Matching - [How matching works](https://docs.furniops.com/matching): How FurniOps decides that two sources' records are the same item. Deterministic identifiers first, then a trained probabilistic model with embeddings, then an AI judge that must quote its evidence, and people for everything in doubt. - [Review and merges](https://docs.furniops.com/matching/review): The pairs matching could not decide alone, and the merges it and your people made. Reading a pair's evidence and the AI judge's answer, merging or dismissing it, and undoing any merge. ## Organisations - [Account security](https://docs.furniops.com/organisations/account-security): Passkeys, two-step verification with an authenticator app, recovery codes, passwords, sessions, connected apps, and when FurniOps asks you to confirm it is you. - [Audit log](https://docs.furniops.com/organisations/audit-log): The organisation's one timeline of who did what, from sign-in policy to API keys and data operations. What is recorded, what an entry holds, filtering it in the dashboard and the API, and how long it is kept. - [Billing](https://docs.furniops.com/organisations/billing): How an organisation will pay for its plan and FurniOps AI credit. Billing is switched off today. What the billing API and Settings › Billing show, and how subscribing, changing plan, the card, invoices and credit work once selling starts. - [Organisations](https://docs.furniops.com/organisations): How accounts and organisations relate, creating and switching organisations, the organisation's settings, and leaving one or deleting your account. - [Members and roles](https://docs.furniops.com/organisations/members-and-roles): The five built-in roles and exactly what each may do, inviting people, changing roles, removing members, seats, and how a change reaches tokens already issued. - [Single sign-on](https://docs.furniops.com/organisations/sso): Let members sign in with your identity provider over OpenID Connect or SAML 2.0. Verify your domains, connect and test the provider, turn it on, and require it. - [Support access](https://docs.furniops.com/organisations/support-access): How FurniOps support can see what one of your members sees, the limits on it, how every access is recorded in your audit log, and how an organisation blocks it. ## TypeScript SDK - [Change stream](https://docs.furniops.com/sdk/change-stream): changes.stream, a reconnecting async iterable over GET /v1/stream that resumes where it left off. - [Errors and retries](https://docs.furniops.com/sdk/errors-and-retries): The SDK's error classes, exactly when it retries, how long it waits, and how to cancel a call. - [TypeScript SDK](https://docs.furniops.com/sdk): Install @furniops/sdk, create a client, and configure retries, timeouts, versions and credentials. - [Pagination](https://docs.furniops.com/sdk/pagination): PagePromise, for await over every item, page-by-page control, and the change feed's pages. - [Resources and methods](https://docs.furniops.com/sdk/resources): Every SDK method, the API operation it calls, and how arguments map to paths, bodies and queries. - [Webhooks](https://docs.furniops.com/sdk/webhooks): Verify a webhook delivery with the SDK in Node, Next.js, Workers and other runtimes, and type the events you receive. ## Sources - [Cartons](https://docs.furniops.com/sources/cartons): How to write a variant's shipping boxes in one spreadsheet cell, which column settings apply, and how the cartons appear on the product. Sizes, weights and counts, several boxes per cell and several columns per file. - [Categories](https://docs.furniops.com/sources/categories): The FurniOps taxonomy, and how each source's own category paths are filed into it with rules. Suggestions, inheritance, the most specific rule winning, and saving rules through the API. - [Connectors](https://docs.furniops.com/sources/connectors): The vendor data FurniOps reads today beyond files. A Shopify storefront's public feed, and Coaster's dealer API with its separate stock stream. Setup, schedules, credentials and the limits each one keeps. - [Files](https://docs.furniops.com/sources/files): Price lists and catalogues as files. Fetch a file from a URL, upload one through a presigned URL, or let vendors email files to the source's own address, with sender checks and an inbox log. - [Sources](https://docs.furniops.com/sources): A source is one connection to a vendor's data. The kinds of source, what FurniOps can read today, creating one through the API, credentials, schedules, testing, and how FurniOps judges a source's health. - [Mappings](https://docs.furniops.com/sources/mappings): How a file's columns become product, variant and offer fields. FurniOps proposes a mapping for every new set of headers, a person confirms it once, and every later file with those headers imports on its own. Fields, settings, tiers and confirming through the API. - [Probes](https://docs.furniops.com/sources/probes): Before adding a vendor, probe its site. A probe reads a URL politely and reports the platform with its evidence, any wall, the public feed, sitemaps, structured data coverage and the effort to pull it. - [Runs](https://docs.furniops.com/sources/runs): One pull of a source, from fetch to master records. Triggers and streams, the stages a run goes through, how it ends, the cells it could not read, waiting, replaying a snapshot, and starting a run yourself. ## Error codes - [invalid_request](https://docs.furniops.com/errors/invalid_request): 400 Invalid request - [idempotency_key_invalid](https://docs.furniops.com/errors/idempotency_key_invalid): 400 Idempotency-Key header missing or malformed - [unsupported_version](https://docs.furniops.com/errors/unsupported_version): 400 Unsupported FurniOps-Version - [authentication_required](https://docs.furniops.com/errors/authentication_required): 401 Authentication required - [invalid_token](https://docs.furniops.com/errors/invalid_token): 401 Invalid token - [invalid_credentials](https://docs.furniops.com/errors/invalid_credentials): 401 Invalid credentials - [insufficient_user_authentication](https://docs.furniops.com/errors/insufficient_user_authentication): 401 A more recent or stronger sign-in is required - [payment_declined](https://docs.furniops.com/errors/payment_declined): 402 The payment was declined - [permission_denied](https://docs.furniops.com/errors/permission_denied): 403 Permission denied - [reauthentication_required](https://docs.furniops.com/errors/reauthentication_required): 403 Re-authentication required - [sso_required](https://docs.furniops.com/errors/sso_required): 403 Single sign-on required - [insufficient_scope](https://docs.furniops.com/errors/insufficient_scope): 403 Insufficient scope - [approval_required](https://docs.furniops.com/errors/approval_required): 403 A second staff member must approve this first - [connection_required](https://docs.furniops.com/errors/connection_required): 403 Connect to this vendor first - [support_token_invalid](https://docs.furniops.com/errors/support_token_invalid): 403 The support token does not fit - [support_renewal_required](https://docs.furniops.com/errors/support_renewal_required): 401 Renew the support token - [feature_unavailable](https://docs.furniops.com/errors/feature_unavailable): 403 Not included in the plan - [plan_limit_reached](https://docs.furniops.com/errors/plan_limit_reached): 403 Plan limit reached - [plan_required](https://docs.furniops.com/errors/plan_required): 403 A paid plan is needed for live mode - [org_suspended](https://docs.furniops.com/errors/org_suspended): 403 The organisation is suspended - [preview_unsupported](https://docs.furniops.com/errors/preview_unsupported): 422 This operation cannot be previewed - [undo_unavailable](https://docs.furniops.com/errors/undo_unavailable): 422 This change cannot be undone - [test_mode_required](https://docs.furniops.com/errors/test_mode_required): 403 Test mode only - [not_found](https://docs.furniops.com/errors/not_found): 404 Not found - [method_not_allowed](https://docs.furniops.com/errors/method_not_allowed): 405 Method not allowed - [not_acceptable](https://docs.furniops.com/errors/not_acceptable): 406 Not acceptable - [conflict](https://docs.furniops.com/errors/conflict): 409 Conflict - [interaction_superseded](https://docs.furniops.com/errors/interaction_superseded): 409 A newer authorization request replaced this one - [account_mismatch](https://docs.furniops.com/errors/account_mismatch): 409 Signed in as a different account - [connection_exists](https://docs.furniops.com/errors/connection_exists): 409 A connection to this catalogue exists - [connection_limit](https://docs.furniops.com/errors/connection_limit): 409 Too many connection requests - [connection_cooldown](https://docs.furniops.com/errors/connection_cooldown): 409 Wait before requesting this connection again - [agreement_required](https://docs.furniops.com/errors/agreement_required): 409 Accept the data agreement first - [brand_unverified](https://docs.furniops.com/errors/brand_unverified): 409 Verify the brand first - [memory_limit](https://docs.furniops.com/errors/memory_limit): 409 Too many memories - [idempotency_request_in_progress](https://docs.furniops.com/errors/idempotency_request_in_progress): 409 A request with this Idempotency-Key is still being processed - [precondition_failed](https://docs.furniops.com/errors/precondition_failed): 412 Precondition failed - [payload_too_large](https://docs.furniops.com/errors/payload_too_large): 413 Request body too large - [unsupported_media_type](https://docs.furniops.com/errors/unsupported_media_type): 415 Unsupported media type - [unprocessable](https://docs.furniops.com/errors/unprocessable): 422 Request violates a domain rule - [idempotency_key_reused](https://docs.furniops.com/errors/idempotency_key_reused): 422 Idempotency-Key reused with a different request - [currency_unsupported](https://docs.furniops.com/errors/currency_unsupported): 422 Credit is not sold in this currency - [model_unpriced](https://docs.furniops.com/errors/model_unpriced): 422 The model has no price - [price_notice_too_short](https://docs.furniops.com/errors/price_notice_too_short): 422 A price rise needs 30 days' notice - [confirmation_required](https://docs.furniops.com/errors/confirmation_required): 422 Type the confirmation to go ahead - [support_token_required](https://docs.furniops.com/errors/support_token_required): 422 The support token is required - [precondition_required](https://docs.furniops.com/errors/precondition_required): 428 Precondition required - [rate_limited](https://docs.furniops.com/errors/rate_limited): 429 Too many requests - [quota_exceeded](https://docs.furniops.com/errors/quota_exceeded): 429 Monthly quota used up - [ai_budget_reached](https://docs.furniops.com/errors/ai_budget_reached): 429 Today's AI budget is spent - [internal](https://docs.furniops.com/errors/internal): 500 Internal error - [unavailable](https://docs.furniops.com/errors/unavailable): 503 Service unavailable - [overloaded](https://docs.furniops.com/errors/overloaded): 503 Server busy, retry later