Skip to content
Local environment Preproduction — not production data

Changelog

Notable changes to the Factorial Code platform, console, CLI, forms SDK and MCP server, newest first. Each entry is dated by the day it landed on the platform. Changes marked Breaking need action on your side.

Internal infrastructure work (observability, CI, local development) is not listed.

  • Email from an App’s production workspace is delivered. fcode.sendMail (fcode.send_mail in Python) now uses the real relay from the App’s prod- workspace as well as from a customer’s production installation. Executions there used to land in the dev mailbox. Every other workspace still delivers to the dev mailbox.
  • A chat opened on an App page is about that App. The dashboard assistant no longer asks which App you mean.
  • Public forms are a choice again. The Public switch of the form entry point under Trigger from Factorial (factorial.form.public) now decides who can open the form, in both directions: on, anyone with the URL can open and submit it; off, it needs a Factorial user. Until now the switch stored a flag no endpoint read. Forms closed on 14 October stay closed until their App turns the switch on. See Restrict who can open the form.
  • A new form needs a Factorial user unless it says otherwise. Turning a form on records that it requires a Factorial user, unless the same write makes it public, so "form": { "enabled": true } with nothing about access no longer produces a public form. A form already public stays public.
  • Install, settings and uninstall forms cannot be public. The platform keeps an App’s lifecycle forms closed whatever a write asks for, and the console disables the Public switch on them and says why. The install form provisions an installation, so a public one would let anyone with its link do that.
  • The end of an execution survives the log limits. An execution that wrote past its plan’s log limit lost every line after it, usually including how it ended. Now a notice marks where the limit was hit, and when the execution ends its last 5 lines are shown, with how many were left out, followed by the failure reason when there is one. The default limits rise from 500 lines and 500 KB to 10,000 lines and 10 MB per execution.
  • Saving code is no longer blocked by the firewall. A process or module whose source looked like an attack to the firewall in front of the API (<script>, /etc/passwd, SQL or shell snippets) could not be saved. The console, the CLI (@factorialco/fcode-cli 3.5.1) and both Run SDKs now send sourceCode base64 encoded with sourceCodeEncoding: "BASE64", and the API decodes it; responses and stored code stay plain text. An API client of your own keeps working without the field (PLAIN is the default), and can add it if its saves are blocked. Upgrade the CLI if fcode push is.
  • Python modules import at a dotted version tag. fcode.import_module("mail-helper", "1.1.0") failed in the cloud with ModuleNotFoundError: No module named 'mail-helper@1'. Because publishing a workspace version pins every bare import_module to its tag, a Python workspace published as v1.0.0 lost all its module imports. JavaScript and local fcode run were not affected.
  • Only operators change an App’s marketplace visibility. The publication state and the companies allowed to see a private App are operator-only now. App members still see both in the App settings, with the controls disabled.
  • An invalid dependency says why. A manifest that can never install, such as a version that does not exist or a package with no usable wheels, is reported as an error in the manifest rather than a platform failure, and the console’s notification shows the whole message behind Show full error instead of cutting it after three lines.
  • The App page puts the assistant first, with a chat composer under a more compact header. Edit moved into the App’s settings.
  • Breaking — every form requires a Factorial user. Every form open to anyone was switched to require a Factorial user. Forms that customers open from inside Factorial (install, settings and uninstall, an App’s own page, UI trigger dialogs) go through Factorial Actions and are not affected. A form you hand to people with no Factorial account has to be made public again, which works from 16 October.
  • Processes invoked from Factorial speak the user’s language. A bare language such as es, which is what Factorial sends, now reaches a regional locale file such as es-ES instead of falling back to the primary locale. A workspace with no primary locale of its own inherits its parents’ choice, so an installation follows its App. A sub-process started with fcode.processes.run runs in its caller’s locale. fcode run resolves locales the same way from @factorialco/fcode-cli 3.5.1. See Choosing the locale.
  • Executions show who ran them from Factorial. An execution started through Factorial Actions shows Factorial access <id> as its trigger in the executions list and on the execution page, with the company, access, entry point and location on hover. The API returns the same context as factorial (companyId, accessId, entryPointType, locationId) on an execution; it is null for webhooks, schedules and manual runs.
  • Runtimes updated. Cloud executions run on Node.js 22.23 and Python 3.13.16, and JavaScript dependencies install with pnpm 11.28.5.
  • The REST API answers 429 with Retry-After when it is throttled, such as when a client fetches many pages of executions in parallel, instead of a generic 500. Back off for the number of seconds it gives.
  • Apps without OAuth scopes no longer register a Factorial OAuth application, so creating one no longer fails its automatic OAuth setup. Clearing an App’s scopes leaves its existing OAuth application as it was, so customer connections keep working.
  • A slow synchronous execution returns its id. A synchronous execution (async: false, such as a form submission) that ran past 60 seconds answered a generic 500, although it kept running. It now answers 408 with the execution id in Fcode-Execution-ID, in Location and in the body ({status, message, id}), so the caller can follow the result. Forms, webhooks and the process and action APIs pass the 408 on.
  • See how long an execution took to start, and why. Each execution stores its start time split into queue (waiting for a free slot), dependency installation and platform overhead. The executions list draws the split as a strip above the duration, the timeline says what took the time, and the API returns startLatency (queue, dependenciesInstall, overhead, total, in milliseconds) on an execution.
  • Executions start sooner. A process inherited through a pinned parent also has its parameter limits and schema checked against the pinned snapshot that runs, not the parent’s live draft.
  • The MCP server’s tools= option now also limits which tools can be called, not only which are listed. mcpOptions=skipCodingRules now works too, dropping the coding rules from the run_code description. See Tool selection and options.
  • The third-party OAuth callback forwards what the provider sent. Parameters other than code and state (RFC 9207’s iss, Zoho’s location and accounts-server, QuickBooks’ realmId, Shopify’s shop and hmac) reach your onComplete process in providerParams, untrusted input to validate, and providerParamsDropped names anything the platform could not forward. A flow can also set responseType: "omit" for a redirect that issues no code, such as Microsoft’s admin consent or a GitHub App installation. See What the provider sent on its own and Ceremonies that are not a grant.
  • Apps declare the countries they serve. The App settings gain a Countries list (app_country_codes in the App API, ISO codes, empty meaning every country), and Factorial offers the App’s UI trigger buttons only on records of those countries, such as German payroll cycles for a German payroll App. Where the marketplace lists the App is unchanged.
  • The execution timeline shows dependency installs. Installing dependencies that are not preinstalled appears as its own entries, Installing dependencies and Dependencies installed, so install time no longer looks like a slow start.
  • New Apps follow their base workspaces’ stable alias. An App’s workspaces were pinned to the tag stable pointed at on the day the App was created, so moving stable on a base never reached it. They are now pinned to the alias itself and pick up a new stable release on their next execution. Existing Apps were re-pinned the same way on 1 October.
  • Automatically created OAuth credentials are locked. They can no longer be edited by hand, and only operators can delete them; renames and scope changes keep syncing on their own. Editing a credential you entered yourself no longer asks for its secret again.
  • No more 5-second DNS wait in cloud executions. An execution waited about 5 seconds the first time it resolved each new hostname. Lookups now take milliseconds, so a process that connects to three hosts gets to work up to 15 seconds sooner.
  • The console opens on your workspaces. /platform/ is now the workspaces list, with the logo, the docs links and the account menu, and a layout that fits a phone; /platform/teams is gone. A user with a single workspace still goes straight into it.
  • The dashboard assistant changes an App’s OAuth scopes from the chat. Until now it could set them only when creating the App.
  • A long execution can renew FACTORIAL_TOKEN. From 29 October Factorial issues company tokens that expire after one hour. A process gets a fresh one with fcode.refreshFactorialToken() (fcode.refresh_factorial_token() in Python), served to customer installations only, and a client built with createFactorialClient from the factorial-sdk module calls it shortly before the token expires and writes the new one to fcode.env.FACTORIAL_TOKEN. Code that reads the variable without the client does not renew it. Copy token on a dev installation returns a token with at least 20 minutes left. See Token expiry.
  • Declare a file parameter with x-fcode-file. A parameter that takes a file now says so in its data, "x-fcode-file": { "accept": [".msj"] }, next to type, with the extensions it takes in accept. Factorial uploads a file for a Factorial Action only to a parameter declared this way, through an entry point the action is exposed on, and refuses a file whose name matches none of accept. Files uploaded for an action are removed once they are 48 hours old, by a cleanup that runs every 24 hours, so one lives 48 to 72 hours. See file inputs.
  • Deprecated: "ui:widget": "file" as the way to declare a file. The widget is presentation — it tells a form to draw a file picker, and forms still need it for that — so a property that carries only the widget is treated as a file through a fallback that will be removed. Add x-fcode-file to each file parameter; fcode push now prints a warning naming the process and the parameter, with the x-fcode-file to add, and still pushes.
  • The OAuth connect button works on forms embedded on another site. A form using the oauth widget and embedded on a different origin than the forms host no longer reports the connection as cancelled when the provider isolates its login window (as DATEV does): the SDK now relays the completion back to the embedding page. Embedders only need to upgrade the package. (@factorialco/fcode-react-forms 3.4.1.)
  • A form can say it belongs on the App’s page. The factorial block’s form sub-block gained appTool: whether the App offers the form to its users from its own page in Factorial, next to the enabled and public flags. It is the Factorial Action name for the USER_FACING_FORM app role the legacy form block carries, and the platform keeps the two mirrored, so the console now offers a switch for a role that until today only metadata.json and the API could set. Existing forms keep the role they have.
  • One settings block to expose a process to Factorial. A process is now a Factorial Action: the new Trigger from Factorial section of the process dashboard replaces the separate Forms and UI trigger sections, and holds whether Factorial waits for the result, the Factorial policies a user needs to run it, and one switch per entry point — button in the Factorial UI, form, Factorial One tool, backend job. The Factorial One tool carries a tool contract for the assistant: a description, an effect (READ, WRITE or DESTRUCTIVE — unset counts as destructive, so a tool fails closed until you say otherwise), when to use it and when not to, preconditions, what it does not do, how it degrades and which process it previews. The button label is the only text in the block that takes fcode.i18n("key") tokens. The same block is the factorial key of metadata.json, which fcode pull and fcode push round-trip with the usual omit-the-defaults rule, and settings.factorial in the API. The old form and uiTrigger settings stay and are kept mirrored with the new block, so nothing that reads or writes them needs to change now. Processes whose slug is install, settings, uninstall or sync carry a read-only lifecycleRole and show a Lifecycle tag in the processes list and the process header. See Factorial Actions and Process metadata.
  • The datastore is now reachable from outside your processes. The REST API gained a datastore resource offering the same operations fcode.datastore.* does — store, read, delete, list keys, count and expire — against the same entries, so you can seed configuration before a process runs or inspect its state from your own systems. Encrypted values work over REST too: write with "encrypted": true and a later read returns the plain value, exactly as a process sees it. FcodeDatastore is available in both Run SDKs.
  • count and expire are documented. Both have been available in processes for a while and were missing from the docs. count() returns how many entries your workspace holds and expire(key, seconds) gives an existing entry a time to live. See Factorial Code Datastore.
  • Third-party OAuth is handled by the platform. Connecting an external provider used to mean minting your own state, generating a PKCE pair and exposing a public webhook per installation — and it could not work at all with a provider that only accepts a pre-registered redirect URI, because each customer’s workspace has its own webhook URL. Now there is one registered URI for the whole platform, https://code.factorialhr.com/platform/api/oauth/callback. Start a flow with fcode.oauth.start(), hand the returned authorizationUrl to the form’s OAuth widget, and the process you named in onComplete is invoked when the provider redirects back, with the authorization code, the PKCE verifier and your own data already in its parameters. See Connecting a third-party provider.
  • Email from a workspace that is not a production installation is caught, not delivered. fcode.sendMail (fcode.send_mail in Python) now delivers to the recipient only from a customer’s production installation. From every other workspace the call behaves the same but the message lands in the new dev mailbox at https://code.factorialhr.com/devmail/<team-slug>/, where the members of that workspace can read it with their Factorial Code account and nobody else receives it. See Where the email goes.
  • The datastore can encrypt a value at rest. fcode.datastore.set takes an optional third argument: pass true (True in Python) and the value is encrypted with a key that belongs to your workspace before it is written. Reading it back with get is transparent, so nothing else changes, and entry keys are never encrypted. Use it for tokens, credentials or personal data. See Factorial Code Datastore.
  • One version selector for the whole workspace. Versions are no longer browsed per process, module or translation file: a single version selector, the pill in the header of the sidebar’s Develop section, lists Live, the workspace aliases and the workspace version tags, and picking one puts the whole workspace in version mode. The processes, modules and translations lists then show what that version contains — the entities deleted since it was published included and marked as deleted, the ones created after it hidden — every editor shows the snapshot read-only, and Run now, new schedules, the webhook snippet and the form embed code pin to the selected version. A notice heading the page names the version and offers Back to live, the choice is remembered per workspace in your browser, and a link carrying ?version=<tag or alias> arrives on that version. Inherited processes, modules and translations are unaffected: they keep following the version pinned on the parent link. See Browsing and running a version.
  • The per-entity version pages are gone. The Versions tab of a process and of a module, the locale versions page, the dependency-versions settings page and the per-entity Publish version action have been removed: the web application publishes and manages workspace versions only, from the Versions tab of the team settings or the selector’s Create version. The CLI and the API still publish a version of a single process or module, and those versions keep running wherever a tag is accepted.
  • fcode run and fcode http work on Windows (@factorialco/fcode-cli 3.2.1). A local run used to die with spawn npm ENOENT or spawn pnpm ENOENT while installing dependencies, and a run that got past that failed with MODULE_NOT_FOUND because the workspace path lost its backslashes on the way into the generated script. Both are fixed, along with the module search path separator and the environment the process is started with, so a Windows terminal no longer needs WSL. Python processes find the virtual environment’s Scripts\ folder the same way. See Execute processes locally.
  • The create-App wizard picks OAuth scopes for integrations-framework Apps. The wizard now asks for the App type before the scopes: choose Integrations-framework app, pick the data types it syncs (leaves, expenses, worked time, compensations), and the OAuth scopes step opens with the scopes the framework and those data types need already selected. You can still add or remove scopes there, and later from the App’s OAuth tab. See When to use the integrations framework?.
  • Every App folder carries its documentation. fcode team:clone and team:pull now write a README.md, a MARKETPLACE.md (the marketplace listing as front matter plus description), a CHANGELOG.md generated from the App’s releases and the logo under marketplace/ next to each App’s workspace checkout. The README is new on the App page in the dashboard too, on its own README tab. README.md and MARKETPLACE.md go back up with the new fcode app:push, run from the App folder; a pull never overwrites a local edit you have not pushed. See Clone every App of your team.
  • The processes and modules lists sort by name or by last modification in the console. Clicking a column header orders the list by it, and clicking again flips between ascending and descending. Both lists open alphabetically, and the order is part of the URL, so it survives a refresh and can be shared. Sorting covers the whole list rather than the rows already scrolled to, and the processes or modules inherited from a parent workspace are still grouped ahead of the workspace’s own.
  • Forms can complete an OAuth flow. A string or boolean property with "ui:widget": "oauth" renders a Connect button that opens the provider in a popup, learns from the callback how it went and then submits the form, reloads it for the pre-render process to verify the connection, or simply waits for the user — onComplete picks. The callback process ends by redirecting the popup to the SDK’s /sdk/oauth-callback.html. A failed attempt reloads the form in every mode, so the pre-render process mints a fresh authorization URL (the one-time state is spent), and a default on the property renders the button already connected. See Connecting an external account. (@factorialco/fcode-react-forms 3.4.0, @factorialco/rjsf-f0 2.3.0.)
  • Form redirects work on the marketplace. The forms SDK (@factorialco/fcode-react-forms 3.3.0) follows a result’s redirect whether or not the embedder passes onSuccess, and only to http(s) URLs. Marketplace forms therefore redirect after submission, and an error a process returns as errorMessage now shows in the marketplace’s error toast instead of a generic failure.
  • fcode team:clone brings down the team’s global workspaces too. The global workspaces a development team owns are checked out under global-workspaces/{slug} alongside the App folders, and fcode team:pull / team:status cover them like any other workspace. A team folder cloned before this picks them up on its next team:pull. See Clone every App of your team.
  • Breaking — the CLI renames the team topic to settings (@factorialco/fcode-cli 3.0.0). fcode team:* managed the settings of the current workspace, a leftover of the platform calling a workspace a team. That name now belongs to the development team, whose commands arrived alongside it.

    BeforeAfter
    fcode team:pull|push|status|difffcode settings:pull|push|status|diff
    fcode team:versions:* / team:aliases:*fcode settings:versions:* / settings:aliases:*
    fcode settings:get|set|remove (CLI preferences)fcode config:get|set|remove
    team.jsonsettings.json

    A workspace holding a legacy team.json keeps working — it is renamed on first access, with no re-clone and no manual step, and already-cloned workspaces still report as up to date. The old command names are gone rather than aliased, because team:pull and team:status now mean something else. See the CLI reference.

  • Breaking — pin an inherited workspace to a version of its parent. Each parent link takes an optional version: pick one of the parent’s workspace versions — a tag, or an alias such as stable — and everything that parent contributes comes from that release until you move the pin. A pinned parent contributes a strict snapshot: only what it published under that tag, with the snapshot’s content. parentTeamSlugs is replaced by parentTeams, a list of {slug, version} entries, in settings.json, the API and the SDK, with no compatibility shim — update anything that writes the old field. See Workspace hierarchy.

  • Dependencies are versioned — a dependency set is publishable under a tag, aliasable, and published as part of a workspace version, and a pinned execution resolves the packages that version pinned instead of the live manifest. Until now a release shipped frozen code and frozen translations against floating packages: editing the dependencies afterwards silently changed what every published version installed. See Versioning dependencies.
  • fcode diff — a git-style unified diff between the workspace and the cloud, with the cloud as the old side, so the patch reads as what a push would apply. Scoped forms take an optional slug: processes:diff, modules:diff, variables:diff, i18n:diff, dependencies:diff, settings:diff. It exits 1 when local and cloud differ and 0 when they are in sync, so CI can use it as a sync check. See Diff local and cloud changes.
  • fcode status hides inherited resources that are in sync with the cloud, behind a new --showInherited flag, so the rows that need attention are not crowded out. Anything actionable still shows, with a one-line count of what was hidden. fcode diff reports drifted inherited resources the same way.
  • fcode team:clone clones every App of a development team in one command, into a consistent layout with the agent skills installed once and shared across every App. Getting a team’s code onto a laptop no longer means one fcode clone per App with slugs looked up by hand. See the CLI reference.
  • One installation lifecycle on the marketplace — a company’s installation is a single stored status (connection_pending, configuration_pending, active) rather than a status plus a separate connected flag, so the next step — the OAuth popup, the install form, or nothing — is unambiguous. Submitting the install form now advances the installation to active, which nothing on the Factorial marketplace path did before.
  • Workspaces are named after their App — the workspace switcher shows My Legacy HR platform Importer, My Legacy HR platform Importer - dev and My Legacy HR platform Importer - company 125339 instead of dev-1fc5hvvo4ubam2puxuz1y83b7. The slug is unchanged; this is the display name.
  • Per-call locale override — fcode.i18n(key, args, { locale }) resolves that one lookup in another locale, so a single execution can speak several languages, such as emailing each employee in their own. It combines with version pinning, and the fallback chain is never worse than without it: requested locale, then the execution’s locale, then the primary. See Overriding the locale per call.
  • Dependency inheritance — a workspace’s effective dependency set is now its parents’ manifests merged with its own, per package name, child wins. A child workspace can run an inherited process without copying the parent’s manifest in. See Dependencies and Workspace hierarchy.
  • Multi-step forms in a single process — a form declares its steps in the schema’s ui:steps node and the process executes once, after the last step, receiving every parameter as a one-page form would. Chaining separate processes with nextProcessId is no longer needed for a purely visual split. See Multi-step forms.
  • Factorial UI trigger buttons — a process can declare that it renders a button at a registered location inside the Factorial product UI, with its own label and icon, configured from the process settings.
  • one:read and one:write OAuth scopes are available in the scope catalog, so Apps can request them when they are created or edited. See OAuth & API token.
  • fcode syncs workspace versions and aliases — the versions and aliases fields in settings.json are now synced state: fcode settings:status reports local changes, fcode settings:push creates the missing versions and re-points aliases, and fcode settings:pull no longer wipes them (the commands were fcode team:* until 28 August). See the CLI reference and Workspace versioning.
  • Factorial OAuth applications are provisioned automatically for every App, for both development and production. Creating a Doorkeeper application by hand and pasting the client id and secret back into the console is no longer part of the setup.
  • Inherited resources are hidden by default in the processes, modules, i18n and variables lists, behind a “Show inherited” switch, and the three lists that lacked it gained name search. The switch is not shown on a workspace with no parents.
  • Breaking — execution messages are markdown (fcode-react-forms 3.0.0, rjsf-f0 2.0.0). A form result message / errorMessage is rendered as GitHub Flavored Markdown; raw HTML is dropped. GFM tables render as an f0 table with sticky headers and Excel/CSV export. schema.<field>.rawHtml.before/after is replaced by schema.<field>.markdown.before/after. Rewrite HTML messages as markdown.
  • keepSuccessView on FcodeForm — an embedder that passes its own onSuccess can keep the default success view (the markdown result message) instead of replacing it. Marketplace form pages now show it.
  • Developer, support and help links on marketplace listings. The vendor is derived from the owning team, and the support and help links are declared on the App’s Marketplace tab.
  • Set the stable release — App members choose which deployed release is the stable one, and the stable alias on the App’s production workspace follows. Customer deploy workspaces resolve it through inheritance. The first deployed release becomes stable automatically.
  • One Install button on the marketplace — Connect and Install are a single action. It redirects through OAuth when there is no connection yet, then provisions the deploy workspace and renders the install form. Uninstalling now revokes the connection and the deploy workspace’s FACTORIAL_TOKEN, so a reinstall never reuses an old credential.
  • Parent order is the inheritance precedence — a team’s parent list is ordered and the first parent wins conflicts, for processes, modules, variables and locales. Reordering parents is a real change: fcode status detects it and fcode push persists it. See Workspace hierarchy.
  • Filter the App list by requested OAuth scopes, marketplace visibility and language, on top of the existing status, team and framework filters.
  • Internationalization — a workspace holds one YAML file per language under i18n/, and process code, module code and form schemas resolve keys through fcode.i18n("key", args). A workspace picks a primary locale used as the fallback, a missing key resolves to itself so a gap never breaks an execution, and locales can be pinned to a version. Locales inherit from parent workspaces key by key rather than file by file. See Internationalization.
  • Cmd+S / Ctrl+S saves on every screen with a save bar. Monaco keeps owning the shortcut inside a code editor.
  • Team variables inherit from parent workspaces — a workspace uses the variables defined in its parents and overrides any of them with its own value, resolved current-workspace-first then parents in order. The old dashboard workaround that copied parent variables into each new workspace — secrets as a placeholder someone filled in by hand — is gone. See Team variables.
  • The web client runs on the f0 design system — every screen renders f0 components, aligning the console with the rest of Factorial.
  • @factorialco/rjsf-f0 supports f0-react 6 alongside 4, so a host on either major can pass rjsfTheme={Theme} against a single f0-react instance. Released as 1.1.0, no component code changed.
  • The active tab lives in the URL on the App detail, App settings and Team detail pages, so a tab can be linked and shared.
  • The forms SDK packages are on npm — @factorialco/fcode-react-forms and @factorialco/rjsf-f0 are published to the registry, so consumers install them normally instead of vendoring a tarball. See Installation.
  • Forms render with f0 components — a new standalone @factorialco/rjsf-f0 theme renders real f0 components on f0 surfaces. The hosted forms.js embed, which loads on arbitrary third-party pages, keeps the dependency-free stylesheet theme. See Customization.
  • Reworked App setup checklist and environment tabs — the tabs follow the App lifecycle (Development, Production, Publication), OAuth is tracked as two steps rather than one, and a new informational “Build the App locally” step opens the CLI commands prefilled with the App’s dev workspace slug. See the developer journey.
  • Request a DatoCMS link — App members can ask for their marketplace metadata to be linked to its DatoCMS record instead of only seeing read-only text.
  • Per-company development deployments — an App in development can be installed into a specific demo company and exercised through the marketplace before it is released. A company can hold a dev and a prod deployment of the same App; the dev deploy workspace inherits live dev code with no release pinning.
  • Workspace versioning — creating a workspace version publishes the same tag on every process, module and locale the workspace owns, and workspace aliases such as production manage the per-entity aliases across all of them. Inherited entities are never touched, and per-entity versioning keeps working independently. A partially failed release is safe to retry. See Workspace versioning.
  • Pin a version without a header — webhooks and the four form endpoints accept a version_tag query parameter, which takes precedence over Fcode-Version-Tag, and an embedded form accepts the version as an attribute. Third-party senders that only let you configure a URL can now target a version. See Process versioning.
  • Breaking — workspace-level webhook authentication. A webhook is public, protected by the workspace configuration, or protected by its own header and variable. The header name is configurable, so senders that cannot use Authorization: Bearer — including Factorial’s own webhook sender — can authenticate. Per-process authVariable becomes an authMode plus a {headerName, variableKey} pair. See Webhooks.
  • The founding member of a team is its admin — the requester who creates a development team joins as admin and can invite members and change roles without an operator. Someone approved into a team that already has members still joins as developer. See Roles & permissions.
  • UI/UX pass over the console and the marketplace — searchable workspace pickers in the clone dialog, tighter list columns, and marketplace pages that use the standard console chrome.
  • Breaking — webhooks are protected by a team variable. Basic auth on process webhooks is replaced by a single named team variable: callers send its value as Authorization: Bearer <token>. A missing header, a token mismatch or an undefined variable all return 401. See Webhooks.
  • Forms can require a Factorial user token — a form opts in with authMode = FACTORIAL, after which the form endpoints validate a credential instead of being fully public. Forms that run app code on customer data should opt in.
  • Processes are referenced by slug in generated embed snippets, playground links and the console’s copy-link action. A slug survives a move between workspaces, where a UUID does not. Existing id-based embeds keep working.
  • fcode syncs the form auth mode — form.authMode round-trips through processes/<slug>/metadata.json, so a protected form is no longer pulled to disk as a public one, and the mode can be changed from the repository.
  • The App detail page follows the setup journey — tabs reordered, and a collapsible “Set up this App” checklist with per-step state and a progress bar replaces the stacked warnings.
  • Marketplace inside the console — Factorial customers sign in with Factorial OAuth, browse Apps published to production, install them for their company and manage what they installed.
  • Private Apps — an App can be marked private with an allowlist of company ids, and is then only listed and installable by those companies, on both marketplace surfaces.
  • OAuth scopes are optional when creating an App — an App that never calls the Factorial public API no longer has to request a scope, and its setup checklist can reach completion. See OAuth & API token.
  • Global workspaces can be owned by a team — the owning team’s roster gets access, so the workspace appears in the web client’s team selector and can be picked as a parent workspace.
  • Forms show a loading placeholder sized to the real form while the schema loads, instead of a zero-height container that pops into a full form. A form with a preRenderProcess waits seconds rather than milliseconds, so the skeleton matters.
  • Deployments list and end-user actions — a Deployments page listing the team’s deployments, filterable by App and company id. The deployment detail page shows copyable links to share with the customer’s end users: the OAuth authorization link and each form-enabled process, grouped by app role.
  • UNINSTALL app role — a process can be marked as the App’s uninstall action, alongside INSTALL, SETTINGS and USER_FACING_FORM.
  • Form file uploads expire after 1 hour instead of a week. They are short-lived intake artifacts. See Storage.
  • fcode clone installs the agent skills non-interactively, targeting Claude Code reliably in a fresh workspace. See Skills.
  • Requesting access is separate from creating an App — the public form creates an access request, and approval provisions only the development team and the requester’s account. A team member then creates Apps themselves, provisioned immediately with no approval step.
  • Square logo per App, uploaded from the Edit App dialog and served publicly for marketplace branding.
  • OAuth scopes are editable after the App is created, not only in the creation wizard.
  • Developers see the New deployment action on the App detail page; it was gated to operators in the UI even though the API always allowed it.
  • fcode round-trips the app role — appRole is read, stored in processes/<slug>/metadata.json and written back, so a role set in the console survives a pull and can be changed from the repository.
  • App roles on process form settings — a process declares whether its form is the App’s INSTALL form, its SETTINGS form, a USER_FACING_FORM, or NONE. The role travels through the release and inheritance chain with the process.
  • Marketplace metadata and screenshots editor in the console: markdown description, tagline, categories and screenshots per App.
  • MCP clients can register themselves — the MCP server returns the resource_metadata URL on 401s and serves resource-specific protected-resource metadata, so OAuth discovery and dynamic client registration work in clients like VS Code without a manual client-id flow. See the MCP server quickstart.
  • fcode clone --autoSkillsSetup installs the agent skills with defaults and no prompts, for scripted clones. Also available as a persistent setting.
  • Enter submits dialog forms in the console, where it previously did nothing.
  • Copy the dev FACTORIAL_TOKEN from the App’s OAuth tab, so it can be used when cloning the workspace to a local workstation. See API credentials.
  • preRenderProcess on a form schema — the named process runs while the form is served and its returned variables are merged into the form’s variables, so a single form can compute its own render-time data. The throwaway first step whose only job was to fetch data is no longer needed. See Getting started with forms.
  • App settings are open to App members — managing OAuth credentials, starting the dev OAuth flow and editing the App name and description no longer need an operator. App lifecycle actions (suspend, archive, delete) stay operator-only.