Skip to content

All settings & options

The single reference for every configurable option in the product.

How settings work

Settings live in one Supabase table, workspace_settings, keyed (workspace_id, module_id) with a settings jsonb column. The engine is core/settings/index.js (settingsManager):

  • Schemas are in-memory — at boot, registerModuleSettings folds each manifest's settingsSchema into the manager.
  • Resolution: schema defaults ← stored overrides. json fields deep-merge (default ← stored) so partial objects inherit default siblings; arrays replace; mergeStrategy: 'replace' opts out.
  • Unknown keys are rejected on write.

Registered module_id rows: global, 1a-blog-publisher, orbit-pixel, pages, backlinks, health, ai-visibility, agent-autopilot, social, sales, market. No row: gsc (namespaces into 1a-blog-publisher), lead-audit, web, mcp (account-level mcp_tokens table, no workspace state).

Namespaces (core/settings/namespaces.js): profile, publishing, agent, gsc are projections over the ONE frozen '1a-blog-publisher' row — a named subset of its keys, not separate rows. Invariant (boot + npm test): the four namespaces form a total, disjoint cover of the schema.

No reserved enabled toggle anymore: the per-workspace module switch (an enabled boolean injected per schema at registration) was removed 2026-08-14 — every module is available to every workspace, schemas register exactly as declared, and enabled keys still stored in old rows are inert (nothing reads them).

global defaults (core/settings/defaults.js)

KeyTypeDefaultControls
workspaceNamestring'Default Workspace'Workspace display name
timezonestring'UTC'Scheduling + date formatting (cron stagger, cost-cap day boundary)
languagestring'en'Primary UI language
localestring'en-US'Date/number formatting
targetCountriesstringnullComma-separated target country codes

The 1a-blog-publisher row (Collections engine + workspace identity)

Set-via legend: O = onboarding wizard, S = Settings UI, R = runtime/system, I = internal/operator.

Namespace profile — identity, audience, lifecycle

KeyTypeDefaultControlsVia
companyNamestringnullBrand name (required; used in all prompts + UI)O/S
companyDescriptionstringnullOperator "what we do" line; fallback when brandIdentity missingO/S
industrystringnullIndustry/niche labelO/S
productsstringnullProduct/service list (prompt input)O/S
languagesjsonnullOrdered ISO 639-1 codes; first = primary. Required at runtimeO/S
serviceAreasjson[]ISO country codes; [0] drives the search market keywords are validated in (the vendor location code)O/S
idealClientstringnullLong-form ICP — canonical audience anchor in every AI prompt; never AI-overwrittenO/S
visibilityTargetsjsonnullSearches the workspace wants visibility forO/S
competitorsjsonnullCompetitor hostnames — filtered from research + rendered HTMLS
brandVoiceNotesByLocalejsonnullPer-locale free-text voice notesS
avgLeadValueCurrencystringnullISO 4217 workspace display currency — every money amount renders in it (onboarding Localization slide + Settings → Workspace). avgLeadValue was retired 2026-08-20: value is per conversion goal (pixel_conversion_goals.value_amount, required at setup); the legacy key self-strips from stored rows on readO/S
brandIdentitystringnullAI-generated company identity paragraph (primary locale)O(AI)/S
brandIdentityByLocalejsonnullLocalized identity paragraphsO(AI)/S
brandVoiceByLocalejsonnullDense per-locale voice reference pasted into the writer prompt; re-extractableO(AI)/S
derivedBrandTermsjsonnull{ hash, terms[] } — brand/product NAMES the offer judge extracted from the person's own profile text (signal plan step 4), pinned by the territory hash; merged into gsc/brand.js's branded split. Derived, never editedO(AI)
lastSiteRefreshAtstringnullISO timestamp of last site refreshR
experiencestring'agency'Which SURFACE this workspace gets: agency = the Signal board (today's product) · content = board hidden, banked ideas as a plan, ops alarms only. Operator-set (internal dashboard → workspace popup → Experience) because it maps to what they pay for. Unknown/absent fails OPEN to agency. Not a cost lever — the pipeline is identical in both modes; the agent level ladder is the cost control. Resolution is mode × viewer in workspace/experience.js (resolveSurfaces), so agency STAFF keep the board inside a content workspaceO
onboardingStatus/StartedAt/CompletedAt/Errorstringnull/setup orchestration state (running/complete/failed); settings PATCH refused while runningR

Namespace publishing — production config + CMS credentials

KeyTypeDefaultControlsVia
imageDimensionsjsonnullCover-image resize { width?, height?, fit? }S
cmsTypestringnullpayload/wordpress/webflow/drupal/shopify/wix/custom (required)O/S
payloadApiUrl, payloadApiKeystringnullPayload credentialsO/S
wordpressApiUrl, wordpressUsername, wordpressAppPasswordstringnullWordPress credentialsO/S
webflowApiToken, webflowSiteIdstringnullWebflow credentialsO/S
drupalSiteUrl, drupalClientId, drupalClientSecret, drupalOauthScopestringnullDrupal OAuth credentialsO/S
shopifyStore, shopifyClientId, shopifyClientSecret, shopifyAuthorNamestringnullShopify credentials + authorO/S
wixApiKey, wixSiteId, wixMemberIdstringnullWix credentials + author memberO/S
sanityProjectId, sanityDataset, sanityApiTokenstringnullSanity credentials (Editor token; dataset usually production)O/S
customEndpointBaseUrl, customEndpointAuthHeader ('Authorization'), customEndpointAuthSecret, customEndpointBodyFormat ('markdown'), customEndpointRoutesstring/jsonnullCustom-endpoint config; {id} substituted in routesO/S

The publishing target (base URLs, collection, field/taxonomy mapping) lives on workspace_collections, not here.

Namespace gsc — Search Console

KeyTypeDefaultControlsVia
gscSiteUrlstringnullGSC property URL (required)O/S
gscServiceAccountJsonstringnullLegacy per-workspace service account (new workspaces use the shared account)S
gscVerifiedbooleanfalseConnection tested OKR
cronGscSyncstring'0 5 * * *'Daily GSC sync scheduleS/I

Namespace agent

KeyTypeDefaultControlsVia
agentjson{ paused: false }agent.paused = the workspace's ONE off-switch: every scheduled job for the workspace — agent lanes AND module crons (syncs, audits, refresh, rollups) — is skipped by the scheduler chokepoint (workspace/readiness.js#automationGate, read fresh at every fire), so no AI spend; chat always works. Written by the app's "Pause agent" toggle (POST /api/agent/:ws/status) and the internal dashboard's level ladder (off). agent.dailyCostCapUsd (default $15/workspace/day via core/spend; enforced inside every paid provider client, so it covers LLM, search-data, research and embedding spend on every path — chat, crons, routes and MCP alike, not just agent dispatch); agent.thresholds overrides classification cuts. Deep-merged on partial writeS (pause) / I (cap)

Empty-schema module rows

Rows that exist by the shell-module convention (a module that owns a row declares settingsSchema: {}); nothing is configurable on them. The per-module paused "operator hatch" these rows were said to carry was never in any schema — settingsManager.set rejected the key — and was removed 2026-08-16; the ONE per-workspace pause is agent.paused above (the injected enabled switch was removed 2026-08-14 — every module runs everywhere):

module_idNotes
orbit-pixelFrozen id
pages, backlinks, health, ai-visibilityWeekly crons, fixed schedules
agent-autopilotThe agent's cron seam; the agent's own knobs stay in the agent namespace
social, sales, marketShells (501)