UiPath Coded Functions
Atomic, deterministic units of business logic — no LLM reasoning, no agent loop — written in Python or TypeScript/JavaScript and shipped as first-class UiPath artifacts. One uip function CLI drives both languages. A Coded Function takes typed input, executes deterministic code, and returns typed output; use one when generic activities don't cover the required logic (custom-auth API calls, domain rules, ERP queries via Integration Service, data transforms).
When to Use This Skill
- Scaffold, author, or modify a coded function in either language (
uip function new <NAME> -l py|ts|js)
- Declare typed contracts — Pydantic Input/Output (Python) or
defineSchema<T>() / JSON Schema literals (JS/TS)
- Test locally:
uip function run (both languages), uip function serve + curl (JS/TS HTTP endpoints)
- Error handling: returned error fields (Python),
FunctionError/status codes and job fault semantics (JS/TS)
- Call UiPath platform APIs from inside a function (Python SDK;
@uipath/uipath-typescript with ctx tokens)
- Wire a Coded App frontend to a JS/TS function backend (tokens, CORS, timeout budget, local dev loop)
- Pack, publish, invoke in production; register in a solution; resource bindings
- Debug deployed failures: cold-start hangs, errorCode 4801/4804/1623, missing tokens, entrypoint errors
Do NOT use this skill for:
- LLM/agentic projects (
agent.json, LangGraph, LlamaIndex, OpenAI Agents, agent loop) → uipath-agents
- The Coded App frontend itself (React/Vite app code, PKCE setup, app deploy) →
uipath-coded-apps
Language Split
Detect an existing project by its signals before doing anything; for a new project the language follows the caller's stack.
|
Python |
JS/TS |
| Scaffold |
uip function new <NAME> -l py (-l py required) |
uip function new <NAME> -l ts / -l js (ts is the CLI default) |
| Project signals |
pyproject.toml + uipath.json functions map |
package.json + uipath.json functions map |
| Contracts |
Pydantic BaseModel / @dataclass Input/Output |
defineSchema<T>() (TS) / JSON Schema literal (JS) |
| Entrypoints |
"main": "main.py:<fn>", generated by uip function init |
functions/<FILE>.ts:default, auto-synced; no init |
| Calling mode |
run-as-job (Maestro, agents, Orchestrator API) |
one declared mode: HTTP endpoint or run-as-job — never both |
| Local dev |
uip function run |
uip function serve (HTTP on :7070) / uip function run (one-shot job) |
| References |
references/python/ |
references/js/ |
Job-startable functions are invoked from Maestro BPMN/Flow (Service Task), coded agents (as a tool or step), other functions, the Orchestrator API (POST /Jobs/StartJobs), or schedules/triggers. A JS/TS HTTP-mode function is instead called synchronously through its Orchestrator HTTP trigger (references/js/http-semantics-guide.md).
Critical Rules
Both languages
- No LLM calls inside a Coded Function. LLM reasoning breaks the deterministic contract — that project is an agent →
uipath-agents.
- The
functions map in uipath.json identifies the project as a Coded Function — it is the marker every tool reads.
- Cloud-backed work needs auth:
uip login --organization "<ORG>" --tenant "<TENANT>" --output json.
Python
UiPath() must never be instantiated at module level — lazy singleton inside a getter.
- Errors are returned, not raised: populate
error_type/error_message output fields; never let exceptions bubble out of the entrypoint.
uip function init must run before pack or push — it generates entry-points.json, bindings.json, project.uiproj; re-run after any schema or entrypoint change.
- Typed I/O is mandatory — Pydantic
BaseModel, pydantic.dataclasses.dataclass, stdlib @dataclass, or a thin typed class; apply @traced(name=..., run_type="uipath") to the entrypoint for LLM Ops Traces.
pyproject.toml needs authors (else pack rejects: Project authors cannot be empty) and no [build-system] section.
- The scaffold follows installed packages: with an agent framework present,
uip function new -l py emits an agent scaffold — reshape it (see references/python/workflow-guide.md Step 1), don't re-run new.
JS/TS
- Throw
FunctionError, never plain Error. A plain throw is always a generic 500 (JsCodedFunction.HandlerError) that leaks the raw stack; FunctionError(message, status) carries an author-controlled status (pass errorCode in options to set the job error code). Argument order is (message, status). See error handling.
- Contracts are schema-first.
defineSchema<T>() over interfaces (TS, lowered at build time) or a bare JSON Schema literal (JS). Do not use zod/arktype/valibot for new functions — they break static contract extraction and add a runtime dependency. No $ref, any, bigint, or tuples; the schema literal must be static.
- One default-exported
defineFunction per file, directly under functions/. Helper modules are _-prefixed (functions/_helpers.ts). method + path together or neither — the pair selects the function's single calling mode: HTTP endpoint or plain run-as-job, never both. Logic needed on both surfaces gets two thin functions sharing a _-helper.
- Intra-project imports carry the
.ts extension (./_helpers.ts). Extensionless imports resolve in local dev but hang the whole runtime at production cold start, with no logs.
- Runtime deps go in
dependencies, public npm only; the SDK stays in devDependencies. Production runs npm install --omit=dev at cold start — a runtime import from devDependencies or a private registry crashes/hangs the function. Regenerate the lockfile after any dependency change; a stale package-lock.json fails every route with errorCode 4801.
- Finish in <20 s — the gateway times out at 25 s and returns a
303 to a polling URL without CORS: a browser caller loses the result permanently, and recovering it server-side via the redirect is undocumented. Use AbortSignal.timeout(...) on every external call; move longer work to a run-as-job function.
- Deployed POST with empty input still needs
body: '{}' — the gateway rejects an empty body with 400 errorCode 4804 (local serve does not, so the bug only appears in production).
- Two tokens, two identities.
ctx.user.accessToken = the caller's token (delegated, caller's folder permissions); ctx.robot.accessToken = the function's own robot identity (S2S, privileged reads). ctx.robot is always null under local serve (ctx.user only when no Bearer token is sent) — fall back to process.env["UIPATH_ACCESS_TOKEN"].
- Read platform coordinates from
ctx.platform (baseUrl, orgId, tenantId, folderKey), never from input fields — caller-controlled URLs are a redirect risk. Env vars (UIPATH_BASE_URL, UIPATH_ORG_ID, UIPATH_TENANT_ID) are the local-only fallback.
- Only the SDK
logger.* reaches Orchestrator job logs. console.* prints locally and is not forwarded.
- No
Buffer. Use Uint8Array, TextEncoder, TextDecoder.
- After
uip function publish, manually update the Function Release in Orchestrator (Automations → Processes) — triggers only sync to the new version once the release is updated.
uip function run is a one-shot job execution, not an HTTP call. To exercise the HTTP surface locally, curl/fetch against the serve server — no CLI subcommand invokes a route for you.
Quick Start
Python
uip function new <NAME> -l py # scaffold (agent-framework packages hijack the scaffold — see workflow guide Step 1)
# author: Pydantic Input/Output + @traced entrypoint, lazy UiPath() singleton, errors returned not raised
uip function init # generate entry-points.json / bindings.json / project.uiproj
uip function run <ENTRYPOINT> '{"document_id": "42"}'
uip function pack && uip function publish
Full workflow (schema, template, uipath.json registration, pyproject.toml, SDK capabilities, attachments, packOptions): references/python/workflow-guide.md.
JS/TS
uip function new <NAME> -l ts # or -l js; --empty for no sample
# author: one default-exported defineFunction per functions/ file — method+path for HTTP, neither for run-as-job
uip function serve # HTTP on :7070, hot reload; test with curl
uip function run --function <NAME> --input '{}' # one-shot local job execution
uip function pack && uip function publish # then update the Function Release in Orchestrator
// functions/invoice.ts
import { defineFunction, defineSchema, FunctionError } from "@uipath/coded-functions-js-sdk";
interface Input {
invoiceId: string;
/** @default 0 */
amount?: number;
}
interface Output {
approved: boolean;
}
export default defineFunction({
name: "approve-invoice",
method: "POST",
path: "/invoice", // omit method+path entirely for a run-as-job function
input: defineSchema<Input>(),
output: defineSchema<Output>(),
handler: async (input, ctx) => {
if (!ctx.user?.accessToken) throw new FunctionError("Unauthorized", 401);
return { approved: input.amount! < 10_000 }; // success data only — errors are thrown
},
});
Authoring detail, local dev, HTTP semantics, deployment, bindings, Coded App wiring: references/js/ (navigation below).
CLI Reference
uip function new <NAME> -l py|ts|js [--empty] # scaffold (TypeScript default; --empty is JS/TS only)
uip function init # Python only — entry-points.json, bindings.json, project.uiproj
uip function serve [--port 7070] [--runtime node|deno] # JS/TS only — local HTTP server, hot reload
uip function run # both languages — one-shot local execution
uip function pack [--nolock] # build the .nupkg
uip function publish [--feed-id <FEED_ID>] # upload package to a process feed
uip function push --project-id <PROJECT_ID> # sync sources to a Studio Web project
uip function runtime-install # JS/TS — one-time runtime pre-install (otherwise downloaded on first serve/run)
The retired plural spelling (functions) is not available — always write the singular uip function.
Reference Navigation
| I need to… |
Read |
| Python: full workflow — scaffold, schema, template, registration, dependencies, init, SDK calls, attachments, pack |
python/workflow-guide.md |
| JS/TS: write handlers, contracts, ctx, errors, logging |
js/authoring-guide.md |
| JS/TS: run and test locally (serve, run, env, tokens) |
js/local-dev-guide.md |
| JS/TS: routing, status codes, deployed limits |
js/http-semantics-guide.md |
| JS/TS: run-as-job mode, fault semantics |
js/job-mode-guide.md |
| JS/TS: pack, publish, invoke in prod, solutions, cold start |
js/deployment-guide.md |
| JS/TS: bindings — entry-points.json, bindings_v2.json, overrides |
js/bindings-guide.md |
| JS/TS: call UiPath APIs from a function (uipath-typescript, IS) |
js/calling-uipath-apis-guide.md |
| JS/TS: wire a Coded App frontend to a function backend |
js/coded-app-wiring-guide.md |
Anti-patterns
Python
- Instantiating
UiPath() at module level — always a lazy singleton inside a getter (Python Rule 1).
- Raising exceptions from the entrypoint — populate the error output fields and return (Python Rule 2).
- Skipping
uip function init after a schema change — stale entry-points.json ships wrong contracts (Python Rule 3).
JS/TS
throw new Error("…") — generic 500, raw stack leaked, no author-controlled status — JS Rule 1.
- Returning an
errors[] array inside a 200. Output schemas carry success data only; a function throws one error at a time.
- zod/arktype contracts on new functions — break static extraction, drag a runtime dependency — JS Rule 2.
- Extensionless relative imports — work locally, hang production cold start — JS Rule 4.
- Runtime dependency in
devDependencies or on a private registry — skipped or unfetchable at cold start — JS Rule 5.
- Adding
server.proxy to the Coded App's Vite config to reach the function — it breaks the app's OAuth callback; fetch http://localhost:7070/<PATH> directly (serve sends CORS *).
- Accepting
baseUrl/orgId/tenantId as function input — redirect risk; use ctx.platform — JS Rule 9.
- Calling the portal domain from a browser — no CORS there; browsers must call the
api.<HOST> subdomain.
console.log for production diagnostics — never reaches job logs — JS Rule 10.
- Building the invoke URL from the trigger's own Id — 404 errorCode 1623; the URL takes the folder Key GUID + package id + slug.
- Expecting a caller to recover a result after 25 s — keep HTTP functions under 20 s, move longer work to a run-as-job function — JS Rule 6.
1---2name: uipath-functions3description: UiPath Coded Functions — deterministic Python or TypeScript/JavaScript units built with the `uip function` CLI (`new -l py|ts|js`, `init`, `serve`, `run`, `pack`, `publish`); the `functions` map in `uipath.json` marks the project. Python: Pydantic Input/Output models, lazy `UiPath()` SDK singleton. JS/TS: `defineFunction` + `defineSchema<T>()` contracts, HTTP endpoints for Coded App backends or run-as-job, `FunctionError`. Rule-based logic, data transforms, ERP/Integration Service calls — invoked as Maestro Service Tasks, from agents, via Orchestrator `POST /Jobs/StartJobs`, or HTTP triggers; no LLM reasoning or agent loop. For LLM/agentic projects (LangGraph, LlamaIndex, OpenAI Agents, `agent.json`)→uipath-agents. For the Coded App frontend itself (React app code, PKCE setup, app deploy)→uipath-coded-apps.4---56# UiPath Coded Functions78Atomic, deterministic units of business logic — no LLM reasoning, no agent loop — written in **Python** or **TypeScript/JavaScript** and shipped as first-class UiPath artifacts. One `uip function` CLI drives both languages. A Coded Function takes typed input, executes deterministic code, and returns typed output; use one when generic activities don't cover the required logic (custom-auth API calls, domain rules, ERP queries via Integration Service, data transforms).910## When to Use This Skill1112- Scaffold, author, or modify a coded function in either language (`uip function new <NAME> -l py|ts|js`)13- Declare typed contracts — Pydantic Input/Output (Python) or `defineSchema<T>()` / JSON Schema literals (JS/TS)14- Test locally: `uip function run` (both languages), `uip function serve` + curl (JS/TS HTTP endpoints)15- Error handling: returned error fields (Python), `FunctionError`/status codes and job fault semantics (JS/TS)16- Call UiPath platform APIs from inside a function (Python SDK; `@uipath/uipath-typescript` with `ctx` tokens)17- Wire a Coded App frontend to a JS/TS function backend (tokens, CORS, timeout budget, local dev loop)18- Pack, publish, invoke in production; register in a solution; resource bindings19- Debug deployed failures: cold-start hangs, errorCode 4801/4804/1623, missing tokens, entrypoint errors2021Do NOT use this skill for:22- LLM/agentic projects (`agent.json`, LangGraph, LlamaIndex, OpenAI Agents, agent loop) → `uipath-agents`23- The Coded App frontend itself (React/Vite app code, PKCE setup, app deploy) → `uipath-coded-apps`2425## Language Split2627Detect an existing project by its signals before doing anything; for a new project the language follows the caller's stack.2829| | Python | JS/TS |30|---|---|---|31| Scaffold | `uip function new <NAME> -l py` (`-l py` required) | `uip function new <NAME> -l ts` / `-l js` (ts is the CLI default) |32| Project signals | `pyproject.toml` + `uipath.json` functions map | `package.json` + `uipath.json` functions map |33| Contracts | Pydantic `BaseModel` / `@dataclass` Input/Output | `defineSchema<T>()` (TS) / JSON Schema literal (JS) |34| Entrypoints | `"main": "main.py:<fn>"`, generated by `uip function init` | `functions/<FILE>.ts:default`, auto-synced; no `init` |35| Calling mode | run-as-job (Maestro, agents, Orchestrator API) | one declared mode: HTTP endpoint or run-as-job — never both |36| Local dev | `uip function run` | `uip function serve` (HTTP on :7070) / `uip function run` (one-shot job) |37| References | [references/python/](references/python/workflow-guide.md) | [references/js/](references/js/authoring-guide.md) |3839Job-startable functions are invoked from Maestro BPMN/Flow (Service Task), coded agents (as a tool or step), other functions, the Orchestrator API (`POST /Jobs/StartJobs`), or schedules/triggers. A JS/TS **HTTP-mode** function is instead called synchronously through its Orchestrator HTTP trigger ([references/js/http-semantics-guide.md](references/js/http-semantics-guide.md)).4041## Critical Rules4243### Both languages44451. **No LLM calls inside a Coded Function.** LLM reasoning breaks the deterministic contract — that project is an agent → `uipath-agents`.462. **The `functions` map in `uipath.json` identifies the project as a Coded Function** — it is the marker every tool reads.473. **Cloud-backed work needs auth**: `uip login --organization "<ORG>" --tenant "<TENANT>" --output json`.4849### Python50511. **`UiPath()` must never be instantiated at module level** — lazy singleton inside a getter.522. **Errors are returned, not raised**: populate `error_type`/`error_message` output fields; never let exceptions bubble out of the entrypoint.533. **`uip function init` must run before `pack` or `push`** — it generates `entry-points.json`, `bindings.json`, `project.uiproj`; re-run after any schema or entrypoint change.544. **Typed I/O is mandatory** — Pydantic `BaseModel`, `pydantic.dataclasses.dataclass`, stdlib `@dataclass`, or a thin typed class; apply `@traced(name=..., run_type="uipath")` to the entrypoint for LLM Ops Traces.555. **`pyproject.toml` needs `authors`** (else `pack` rejects: `Project authors cannot be empty`) and no `[build-system]` section.566. **The scaffold follows installed packages**: with an agent framework present, `uip function new -l py` emits an agent scaffold — reshape it (see [references/python/workflow-guide.md](references/python/workflow-guide.md) Step 1), don't re-run `new`.5758### JS/TS59601. **Throw `FunctionError`, never plain `Error`.** A plain throw is always a generic 500 (`JsCodedFunction.HandlerError`) that leaks the raw stack; `FunctionError(message, status)` carries an author-controlled status (pass `errorCode` in options to set the job error code). Argument order is `(message, status)`. See [error handling](references/js/authoring-guide.md#errors).612. **Contracts are schema-first.** `defineSchema<T>()` over interfaces (TS, lowered at build time) or a bare JSON Schema literal (JS). Do not use zod/arktype/valibot for new functions — they break static contract extraction and add a runtime dependency. No `$ref`, `any`, `bigint`, or tuples; the schema literal must be static.623. **One default-exported `defineFunction` per file, directly under `functions/`.** Helper modules are `_`-prefixed (`functions/_helpers.ts`). `method` + `path` together or neither — the pair selects the function's single calling mode: HTTP endpoint or plain run-as-job, never both. Logic needed on both surfaces gets two thin functions sharing a `_`-helper.634. **Intra-project imports carry the `.ts` extension** (`./_helpers.ts`). Extensionless imports resolve in local dev but hang the whole runtime at production cold start, with no logs.645. **Runtime deps go in `dependencies`, public npm only; the SDK stays in `devDependencies`.** Production runs `npm install --omit=dev` at cold start — a runtime import from `devDependencies` or a private registry crashes/hangs the function. Regenerate the lockfile after any dependency change; a stale `package-lock.json` fails every route with errorCode 4801.656. **Finish in <20 s — the gateway times out at 25 s** and returns a `303` to a polling URL without CORS: a browser caller loses the result permanently, and recovering it server-side via the redirect is undocumented. Use `AbortSignal.timeout(...)` on every external call; move longer work to a run-as-job function.667. **Deployed POST with empty input still needs `body: '{}'`** — the gateway rejects an empty body with `400 errorCode 4804` (local serve does not, so the bug only appears in production).678. **Two tokens, two identities.** `ctx.user.accessToken` = the caller's token (delegated, caller's folder permissions); `ctx.robot.accessToken` = the function's own robot identity (S2S, privileged reads). `ctx.robot` is always null under local serve (`ctx.user` only when no Bearer token is sent) — fall back to `process.env["UIPATH_ACCESS_TOKEN"]`.689. **Read platform coordinates from `ctx.platform`** (`baseUrl`, `orgId`, `tenantId`, `folderKey`), never from input fields — caller-controlled URLs are a redirect risk. Env vars (`UIPATH_BASE_URL`, `UIPATH_ORG_ID`, `UIPATH_TENANT_ID`) are the local-only fallback.6910. **Only the SDK `logger.*` reaches Orchestrator job logs.** `console.*` prints locally and is not forwarded.7011. **No `Buffer`.** Use `Uint8Array`, `TextEncoder`, `TextDecoder`.7112. **After `uip function publish`, manually update the Function Release** in Orchestrator (Automations → Processes) — triggers only sync to the new version once the release is updated.7213. **`uip function run` is a one-shot job execution, not an HTTP call.** To exercise the HTTP surface locally, curl/fetch against the `serve` server — no CLI subcommand invokes a route for you.7374## Quick Start7576### Python7778```bash79uip function new <NAME> -l py # scaffold (agent-framework packages hijack the scaffold — see workflow guide Step 1)80# author: Pydantic Input/Output + @traced entrypoint, lazy UiPath() singleton, errors returned not raised81uip function init # generate entry-points.json / bindings.json / project.uiproj82uip function run <ENTRYPOINT> '{"document_id": "42"}'83uip function pack && uip function publish84```8586Full workflow (schema, template, `uipath.json` registration, `pyproject.toml`, SDK capabilities, attachments, packOptions): [references/python/workflow-guide.md](references/python/workflow-guide.md).8788### JS/TS8990```bash91uip function new <NAME> -l ts # or -l js; --empty for no sample92# author: one default-exported defineFunction per functions/ file — method+path for HTTP, neither for run-as-job93uip function serve # HTTP on :7070, hot reload; test with curl94uip function run --function <NAME> --input '{}' # one-shot local job execution95uip function pack && uip function publish # then update the Function Release in Orchestrator96```9798```ts99// functions/invoice.ts100import { defineFunction, defineSchema, FunctionError } from "@uipath/coded-functions-js-sdk";101102interface Input {103 invoiceId: string;104 /** @default 0 */105 amount?: number;106}107interface Output {108 approved: boolean;109}110111export default defineFunction({112 name: "approve-invoice",113 method: "POST",114 path: "/invoice", // omit method+path entirely for a run-as-job function115 input: defineSchema<Input>(),116 output: defineSchema<Output>(),117 handler: async (input, ctx) => {118 if (!ctx.user?.accessToken) throw new FunctionError("Unauthorized", 401);119 return { approved: input.amount! < 10_000 }; // success data only — errors are thrown120 },121});122```123124Authoring detail, local dev, HTTP semantics, deployment, bindings, Coded App wiring: [references/js/](references/js/authoring-guide.md) (navigation below).125126## CLI Reference127128```bash129uip function new <NAME> -l py|ts|js [--empty] # scaffold (TypeScript default; --empty is JS/TS only)130uip function init # Python only — entry-points.json, bindings.json, project.uiproj131uip function serve [--port 7070] [--runtime node|deno] # JS/TS only — local HTTP server, hot reload132uip function run # both languages — one-shot local execution133uip function pack [--nolock] # build the .nupkg134uip function publish [--feed-id <FEED_ID>] # upload package to a process feed135uip function push --project-id <PROJECT_ID> # sync sources to a Studio Web project136uip function runtime-install # JS/TS — one-time runtime pre-install (otherwise downloaded on first serve/run)137```138139> The retired plural spelling (`functions`) is not available — always write the singular `uip function`.140141## Reference Navigation142143| I need to… | Read |144|---|---|145| Python: full workflow — scaffold, schema, template, registration, dependencies, init, SDK calls, attachments, pack | [python/workflow-guide.md](references/python/workflow-guide.md) |146| JS/TS: write handlers, contracts, ctx, errors, logging | [js/authoring-guide.md](references/js/authoring-guide.md) |147| JS/TS: run and test locally (serve, run, env, tokens) | [js/local-dev-guide.md](references/js/local-dev-guide.md) |148| JS/TS: routing, status codes, deployed limits | [js/http-semantics-guide.md](references/js/http-semantics-guide.md) |149| JS/TS: run-as-job mode, fault semantics | [js/job-mode-guide.md](references/js/job-mode-guide.md) |150| JS/TS: pack, publish, invoke in prod, solutions, cold start | [js/deployment-guide.md](references/js/deployment-guide.md) |151| JS/TS: bindings — entry-points.json, bindings_v2.json, overrides | [js/bindings-guide.md](references/js/bindings-guide.md) |152| JS/TS: call UiPath APIs from a function (uipath-typescript, IS) | [js/calling-uipath-apis-guide.md](references/js/calling-uipath-apis-guide.md) |153| JS/TS: wire a Coded App frontend to a function backend | [js/coded-app-wiring-guide.md](references/js/coded-app-wiring-guide.md) |154155## Anti-patterns156157### Python1581591. **Instantiating `UiPath()` at module level** — always a lazy singleton inside a getter (Python Rule 1).1602. **Raising exceptions from the entrypoint** — populate the error output fields and return (Python Rule 2).1613. **Skipping `uip function init` after a schema change** — stale `entry-points.json` ships wrong contracts (Python Rule 3).162163### JS/TS1641651. **`throw new Error("…")`** — generic 500, raw stack leaked, no author-controlled status — JS Rule 1.1662. **Returning an `errors[]` array inside a 200.** Output schemas carry success data only; a function throws one error at a time.1673. **zod/arktype contracts on new functions** — break static extraction, drag a runtime dependency — JS Rule 2.1684. **Extensionless relative imports** — work locally, hang production cold start — JS Rule 4.1695. **Runtime dependency in `devDependencies` or on a private registry** — skipped or unfetchable at cold start — JS Rule 5.1706. **Adding `server.proxy` to the Coded App's Vite config to reach the function** — it breaks the app's OAuth callback; fetch `http://localhost:7070/<PATH>` directly (serve sends CORS `*`).1717. **Accepting `baseUrl`/`orgId`/`tenantId` as function input** — redirect risk; use `ctx.platform` — JS Rule 9.1728. **Calling the portal domain from a browser** — no CORS there; browsers must call the `api.<HOST>` subdomain.1739. **`console.log` for production diagnostics** — never reaches job logs — JS Rule 10.17410. **Building the invoke URL from the trigger's own Id** — 404 errorCode 1623; the URL takes the folder Key GUID + package id + slug.17511. **Expecting a caller to recover a result after 25 s** — keep HTTP functions under 20 s, move longer work to a run-as-job function — JS Rule 6.