From e0bd4ab7323384cefde397cb07d6106a3756ae07 Mon Sep 17 00:00:00 2001 From: martmull Date: Thu, 9 Jul 2026 10:41:12 +0200 Subject: [PATCH] docs(apps): make the workspace functions URL the primary route-serving story (#22693) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part 5 of the app-docs audit series (after #22688–#22691). ## The problem `front-components.mdx` warns that the legacy `/s/` route is deprecated and **deactivates on 2026-07-24** (16 days from now), but the rest of the docs still teach `/s/` as the only serving path: `logic-functions.mdx` ("Exposes your function ... under the `/s/` endpoint"), `logic/overview.mdx` ("A request hits your `/s/` endpoint"), and the document-generator tutorial fetches `${TWENTY_API_URL}/s/...` from front-component code. A developer following those pages today ships an app that breaks on Cloud in two weeks. ## What this changes - **logic/logic-functions.mdx** — httpRoute triggers are described as served at the workspace's functions base URL (what the server injects as `TWENTY_FUNCTIONS_URL`; a dedicated per-workspace domain on Cloud, per `WorkspaceDomainsService.buildPublicFunctionBaseUrl`), with a warning box covering the `/s/` deprecation and the self-host fallback. - **logic/overview.mdx** — trigger table no longer hardcodes `/s/`. - **document-generator tutorial** — the `curl http://localhost:2020/s/...` examples stay (they're correct against the local dev image, where no isolated functions domain exists), with a note explaining the Cloud behavior. The front-component code snippets now use the `TWENTY_FUNCTIONS_URL || TWENTY_API_URL + '/s'` fallback pattern — the same one Twenty's own published apps use (e.g. `packages/twenty-apps/public/call-recorder`). Only English sources were touched; `l/` copies come from Crowdin. --- _Generated by [Claude Code](https://claude.ai/code/session_01ExboyDAT19khDuKXaYXETT)_ Review in cubic --------- Co-authored-by: Martin --- .../extend/apps/layout/front-components.mdx | 2 +- .../developers/extend/apps/logic/logic-functions.mdx | 8 ++++++-- .../developers/extend/apps/logic/overview.mdx | 2 +- .../tutorials/document-generator/building-the-ui.mdx | 10 +++++++--- .../apps/tutorials/document-generator/http-routes.mdx | 11 +++++++++-- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/packages/twenty-docs/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/developers/extend/apps/layout/front-components.mdx index 967f85405b..2a1ab9a3ec 100644 --- a/packages/twenty-docs/developers/extend/apps/layout/front-components.mdx +++ b/packages/twenty-docs/developers/extend/apps/layout/front-components.mdx @@ -200,7 +200,7 @@ Front components run browser-side in a sandboxed Web Worker, while [logic functi A logic function declared with `httpRouteTriggerSettings` is reachable over HTTP at its route path. Twenty injects the base URL your functions are served from into the worker as `TWENTY_FUNCTIONS_URL`, together with the `TWENTY_APP_ACCESS_TOKEN` that authenticates the call. There is no dedicated SDK client for invoking your own functions yet, so call them with a plain `fetch`: -> **On Twenty Cloud, HTTP-triggered logic functions are served on a dedicated per-workspace domain** at `https://.twenty.com` — this is exactly what `TWENTY_FUNCTIONS_URL` resolves to. For external callers, copy the exact URL from the function's **HTTP trigger** settings or the application's **Settings** tab. +> **On Twenty Cloud, HTTP-triggered logic functions are served on a dedicated per-workspace domain** at `https://.withtwenty.com` — this is exactly what `TWENTY_FUNCTIONS_URL` resolves to. For external callers, copy the exact URL from the function's **HTTP trigger** settings or the application's **Settings** tab. The legacy `/s/` function route is **deprecated** and will be **deactivated on 2026-07-24**. Use `TWENTY_FUNCTIONS_URL` (above) instead, and migrate any hard-coded `/s/` URLs before that date. The `/s/` route remains available for self-hosting. diff --git a/packages/twenty-docs/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/developers/extend/apps/logic/logic-functions.mdx index ee1a8cc6c7..286c8119a8 100644 --- a/packages/twenty-docs/developers/extend/apps/logic/logic-functions.mdx +++ b/packages/twenty-docs/developers/extend/apps/logic/logic-functions.mdx @@ -51,8 +51,12 @@ export default defineLogicFunction({ ``` Available trigger types: -- **httpRoute**: Exposes your function on an HTTP path and method **under the `/s/` endpoint**: -> e.g. `path: '/post-card/create'` is callable at `https://your-twenty-server.com/s/post-card/create` +- **httpRoute**: Exposes your function on an HTTP path and method at your workspace's **functions base URL** — the value Twenty injects as `TWENTY_FUNCTIONS_URL` (on Twenty Cloud, a dedicated per-workspace domain): +> e.g. `path: '/post-card/create'` is callable at `https://your-workspace.withtwenty.com/post-card/create` + + +The legacy `/s/` prefix route (`https://your-twenty-server.com/s/post-card/create`) is **deprecated on Twenty Cloud** and will be deactivated on **2026-07-24**. It remains available for self-hosted and local instances that don't configure an isolated functions domain — use `TWENTY_FUNCTIONS_URL` when it's set, and fall back to `/s/` otherwise. + To invoke a route-triggered logic function from a (headless) front component, see [Calling a logic function](/developers/extend/apps/layout/front-components#calling-a-logic-function). diff --git a/packages/twenty-docs/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/developers/extend/apps/logic/overview.mdx index fb0107689e..44122a1e87 100644 --- a/packages/twenty-docs/developers/extend/apps/logic/overview.mdx +++ b/packages/twenty-docs/developers/extend/apps/logic/overview.mdx @@ -42,7 +42,7 @@ A logic function picks one or more triggers — every entry below is a separate | Trigger | When it runs | Setting | |---------|--------------|---------| -| **HTTP route** | A request hits your `/s/` endpoint | `httpRouteTriggerSettings` | +| **HTTP route** | A request hits your function's public URL | `httpRouteTriggerSettings` | | **Cron** | A CRON expression matches | `cronTriggerSettings` | | **Database event** | A workspace record is created, updated, or deleted | `databaseEventTriggerSettings` | | **AI tool** | A Twenty AI feature decides to call your function | `toolTriggerSettings` | diff --git a/packages/twenty-docs/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx b/packages/twenty-docs/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx index 7e9b85289e..e554e1785c 100644 --- a/packages/twenty-docs/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx +++ b/packages/twenty-docs/developers/extend/apps/tutorials/document-generator/building-the-ui.mdx @@ -91,9 +91,11 @@ const GenerateDocumentForm = () => { }, []); const generate = async () => { - const apiBaseUrl = process.env.TWENTY_API_URL; + // Prefer the injected functions URL; fall back to the legacy /s prefix (self-hosted/local) + const functionsBaseUrl = + process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL}/s`; const token = process.env.TWENTY_APP_ACCESS_TOKEN ?? process.env.TWENTY_API_KEY; - const res = await fetch(`${apiBaseUrl}/s/documents/generate`, { + const res = await fetch(`${functionsBaseUrl}/documents/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, body: JSON.stringify({ templateId, recordId }), @@ -186,7 +188,9 @@ const DocumentViewer = () => { const recordId = useFrontComponentExecutionContext((c) => c.recordId ?? null); // ...load { content, file } for recordId, then derive the links: const pdfUrl = document.file?.[0]?.url; - const webUrl = `${process.env.TWENTY_API_URL ?? ''}/s/documents/view?id=${recordId}`; + const functionsBaseUrl = + process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL ?? ''}/s`; + const webUrl = `${functionsBaseUrl}/documents/view?id=${recordId}`; // Render the template body, plus quick links to the web page and the PDF. // Links open in a new tab so they don't navigate the embedded component. diff --git a/packages/twenty-docs/developers/extend/apps/tutorials/document-generator/http-routes.mdx b/packages/twenty-docs/developers/extend/apps/tutorials/document-generator/http-routes.mdx index 1b12b86cd3..ac2e61fa95 100644 --- a/packages/twenty-docs/developers/extend/apps/tutorials/document-generator/http-routes.mdx +++ b/packages/twenty-docs/developers/extend/apps/tutorials/document-generator/http-routes.mdx @@ -9,8 +9,15 @@ The same handler can also answer HTTP requests. We'll add two routes: - a **POST** endpoint the UI calls to generate a document, and - a public **GET** endpoint that renders a document as a printable web page. -Both use `httpRouteTriggerSettings`. App routes are served under `/s` on your -Twenty server (e.g. `http://localhost:2020/s/documents/generate`). +Both use `httpRouteTriggerSettings`. On the local dev server, app routes are +served under the `/s` prefix (e.g. `http://localhost:2020/s/documents/generate`). + + +On Twenty Cloud, routes are served on the workspace's dedicated functions domain +— the URL Twenty injects as `TWENTY_FUNCTIONS_URL`, with no `/s` prefix. The `/s` +prefix is deprecated there and only remains for self-hosted and local instances. +See [Calling a logic function](/developers/extend/apps/layout/front-components#calling-a-logic-function). + ## POST route — generate on demand