docs(apps): make the workspace functions URL the primary route-serving story (#22693)
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/<path>`
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/<path>`.
- **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/<locale>` copies come from
Crowdin.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ExboyDAT19khDuKXaYXETT)_
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22693?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
---------
Co-authored-by: Martin <martin@twenty.com>
This commit is contained in:
@@ -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://<your-workspace-subdomain>.twenty.com<path>` — 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://<your-workspace-subdomain>.withtwenty.com<path>` — 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.
|
||||
|
||||
<Warning>
|
||||
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.
|
||||
|
||||
@@ -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`
|
||||
|
||||
<Warning>
|
||||
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 `<server-url>/s/<path>` otherwise.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
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).
|
||||
|
||||
@@ -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/<path>` 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` |
|
||||
|
||||
+7
-3
@@ -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.
|
||||
|
||||
+9
-2
@@ -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`).
|
||||
|
||||
<Note>
|
||||
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).
|
||||
</Note>
|
||||
|
||||
## POST route — generate on demand
|
||||
|
||||
|
||||
Reference in New Issue
Block a user