FurniOpsDocs

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.

A source (src_…) is one connection to a vendor's data: where it comes from, how it is read, how often, and why FurniOps may use it. Every pull of a source is a run, and what the runs read becomes your catalogue's master records.

{
  "object": "source",
  "id": "src_034XqBaL2mRLTZPXyRbaUc",
  "catalogue": "cat_034XqBaL2mRLTZPXyRbaUa",
  "name": "Coaster dealer API",
  "kind": "api",
  "connector": "coaster",
  "config": { "warehouse": "TX" },
  "schedule": { "interval_minutes": 1440, "stock_interval_minutes": 30 },
  "status": "degraded",
  "status_reason": {
    "code": "credentials",
    "message": "The source refused the stored credentials."
  },
  "licence_basis": "tenant_account",
  "has_credentials": true,
  "freshness": {
    "catalogue": {
      "interval_minutes": 1440,
      "changes_per_day": 0.8,
      "observations": 21,
      "learned_at": "2026-10-06T07:00:12Z"
    },
    "stock": {
      "interval_minutes": 30,
      "changes_per_day": 36,
      "observations": 412,
      "learned_at": "2026-10-06T09:00:40Z"
    }
  },
  "inbound_address": null,
  "version": 7,
  "created_at": "2026-09-14T15:02:11Z",
  "updated_at": "2026-10-06T09:00:40Z"
}

What FurniOps can read today

KindWhat it readsCan it run today
fileA CSV, XLSX, XLS, JSON or XML file: fetched from a URL, uploaded, or emailed in.Yes
platform_feedA storefront's public product feed.Shopify: yes. WooCommerce and BigCommerce: accepted, not run yet.
apiA vendor's API, through a connector.The coaster connector: yes.
structured_pageProduct pages' structured data.Accepted, not run yet.
browserPages that need a browser.Accepted, not run yet.
pushData the vendor sends.Accepted, not run yet.

A source FurniOps cannot run yet is kept: the schedule passes it by, and starting a run answers 422. Connectors covers the Shopify feed and Coaster in detail.

Where to manage sources

Sources are created and configured through the API: POST /v1/sources and the calls beside it, which need sources:write (owners, admins and developers). The dashboard's Sources page lists them with their health and freshness; a source's page shows its settings, its runs, and Run now. Creating and editing a source in the dashboard is not available yet.

Create a source

sources.ts
// A vendor's price list at a URL, read every 6 hours.
const pricelist = await furniops.sources.create({
  name: "Acme price list",
  kind: "file",
  config: { url: "https://vendor.example/feeds/pricelist.csv", format: "csv", role: "supplier" },
  schedule: { interval_minutes: 360 },
  licence_basis: "vendor_agreement",
});
console.log(pricelist.id, pricelist.status); // "src_…", "active"
FieldMeaning
nameUnique in the organisation, up to 100 characters.
kindOne of the kinds above. Fixed once the source exists.
connectorFor an api source only: the connector (coaster).
configHow the source is reached. Which fields apply depends on the kind (below).
scheduleWhen runs start on their own. Leave it out and runs start only when asked, or when a file arrives.
licence_basisWhy FurniOps may use the data (below).

A field that does not fit the kind answers 422 with the field named: a feed needs url and platform, an api source needs a connector, and warehouse, price_code and inbound apply to one kind each.

Config

FieldForMeaning
urlfile, platform_feed, structured_page, browserWhere to fetch it, http or https. A file source without one is uploaded or emailed. A push source has none.
platformplatform_feedshopify, woocommerce or bigcommerce.
formatfilecsv, xlsx, xls, json or xml, when the content does not tell.
currencyanyThe currency of prices that name none; usd when unset.
decimalanydot or comma: how to read numbers a column's own values leave undecided (1,234); dot when unset.
roleanyWhose prices the source shows: supplier (dealer cost) or retailer (what a shopper pays). Unset: retailer for storefront kinds (platform_feed, structured_page, browser), supplier otherwise.
warehouseapiThe vendor's warehouse whose stock it reads; the account's default when unset.
price_codeapiThe vendor's price list it reads; the account's own when unset.
inboundfileReceive the source's files by email, from the senders listed. See Files.

Licence basis

Every source says why FurniOps may use its data. It is your statement, and it decides how the data may be shared (see Licences):

ValueMeaning
vendor_agreementYou have an agreement with the vendor.
public_dataThe vendor publishes the data.
tenant_accountIt comes through your organisation's own account with the vendor.

Credentials

A source that signs in to a vendor (an api source) needs credentials. Set them apart from the source, with PUT /v1/sources/{id}/credentials: named values such as api_key, up to 16. They are sealed under the source's ID as they arrive and never returned: the source shows only has_credentials. Setting them again replaces them all; DELETE forgets them, and runs that need them then fail as credentials.

// Coaster's dealer API: the catalogue once a day, stock every 30 minutes.
const coaster = await furniops.sources.create({
  name: "Coaster dealer API",
  kind: "api",
  connector: "coaster",
  config: { warehouse: "TX" },
  schedule: { interval_minutes: 1440, stock_interval_minutes: 30 },
  licence_basis: "tenant_account",
});
// Write-only: sealed as it arrives, never returned. The source shows has_credentials: true.
await furniops.sources.setCredentials(coaster.id, { values: { api_key: "…" } });
const test = await furniops.sources.test(coaster.id);
console.log(test.ok, test.http_status, test.detail);

Test a source

POST /v1/sources/{id}/test reaches the source once, as a run would, and reports what it answered: whether it gave data, the HTTP status, the content type, the time to the response headers, and why it failed. Nothing is stored. A source fetched from a URL is asked for it; an api source's connector signs in with the stored credentials. A file source without a URL has nothing to reach and answers 422.

Schedule

FieldMeaning
interval_minutesMinutes between scheduled runs: 15 minutes to 30 days.
stock_interval_minutesFor an api source whose connector reads stock apart: minutes between stock-only runs, 15 minutes to a day. None when unset.
adaptivetrue by default: FurniOps checks the source sooner when it changes often, never more often than every 15 minutes and never later than your intervals. false keeps them fixed.

How FurniOps learns a source's rhythm, and how to have it check what matters now, is in Freshness.

Change a source

PATCH /v1/sources/{id} changes the name, connector, config (replaced whole), schedule, status or licence basis; the kind is fixed. Send the ETag you read as If-Match (optimistic concurrency).

Status and health

StatusSet byRuns
activeYouScheduled and on demand.
pausedYouNone until you set it active.
disabledYouNone: the source is retired.
degradedFurniOpsStill scheduled. Its last runs failed.
blockedFurniOpsNone until you set it active.

FurniOps judges a source by how its runs end. Only the source's own failures count: a run that failed for any reason but an internal error. A success resets the count; a run that needs a mapping or was aborted changes nothing.

  • Degraded after 3 failures in a row.
  • Blocked when a degraded source has kept failing for 14 days, after 100 failures in a row, or at once when its URL is refused (a private address or a scheme FurniOps will not fetch), since no retry can pass until the URL changes.
  • Recovered to active by its next successful run, while degraded.

status_reason holds the run error behind a degraded or blocked verdict. Setting the status yourself clears it: to unblock a source, fix the cause (the URL, the credentials) and set it active.

Each verdict is an event, delivered by webhooks, the change feed and the stream, with the full source:

EventWhen
source.degradedThe source failed several runs in a row; it is still scheduled.
source.recoveredA degraded source ran successfully and is active again.
source.blockedThe source kept failing (or its URL is refused); no runs until you set it active.

Before you add a source

Not sure what a vendor's site offers? A probe reads it politely and reports the platform, any public feed and the effort to pull it.

On this page