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
| Kind | What it reads | Can it run today |
|---|---|---|
file | A CSV, XLSX, XLS, JSON or XML file: fetched from a URL, uploaded, or emailed in. | Yes |
platform_feed | A storefront's public product feed. | Shopify: yes. WooCommerce and BigCommerce: accepted, not run yet. |
api | A vendor's API, through a connector. | The coaster connector: yes. |
structured_page | Product pages' structured data. | Accepted, not run yet. |
browser | Pages that need a browser. | Accepted, not run yet. |
push | Data 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
// 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"| Field | Meaning |
|---|---|
name | Unique in the organisation, up to 100 characters. |
kind | One of the kinds above. Fixed once the source exists. |
connector | For an api source only: the connector (coaster). |
config | How the source is reached. Which fields apply depends on the kind (below). |
schedule | When runs start on their own. Leave it out and runs start only when asked, or when a file arrives. |
licence_basis | Why 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
| Field | For | Meaning |
|---|---|---|
url | file, platform_feed, structured_page, browser | Where to fetch it, http or https. A file source without one is uploaded or emailed. A push source has none. |
platform | platform_feed | shopify, woocommerce or bigcommerce. |
format | file | csv, xlsx, xls, json or xml, when the content does not tell. |
currency | any | The currency of prices that name none; usd when unset. |
decimal | any | dot or comma: how to read numbers a column's own values leave undecided (1,234); dot when unset. |
role | any | Whose 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. |
warehouse | api | The vendor's warehouse whose stock it reads; the account's default when unset. |
price_code | api | The vendor's price list it reads; the account's own when unset. |
inbound | file | Receive 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):
| Value | Meaning |
|---|---|
vendor_agreement | You have an agreement with the vendor. |
public_data | The vendor publishes the data. |
tenant_account | It 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
| Field | Meaning |
|---|---|
interval_minutes | Minutes between scheduled runs: 15 minutes to 30 days. |
stock_interval_minutes | For an api source whose connector reads stock apart: minutes between stock-only runs, 15 minutes to a day. None when unset. |
adaptive | true 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
| Status | Set by | Runs |
|---|---|---|
active | You | Scheduled and on demand. |
paused | You | None until you set it active. |
disabled | You | None: the source is retired. |
degraded | FurniOps | Still scheduled. Its last runs failed. |
blocked | FurniOps | None 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
activeby 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:
| Event | When |
|---|---|
source.degraded | The source failed several runs in a row; it is still scheduled. |
source.recovered | A degraded source ran successfully and is active again. |
source.blocked | The 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.
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.
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.