Author Custom Functions from your repository
Author Custom Functions in your own repository and push them to Transcend with the CLI or the GitHub Action. Transcend never reads your repo. After the first push, the function appears on the Custom Functions page, and later code changes come from your codebase.
This page is the developer guide. For the dashboard inventory, versioning, and testing UI, see Getting Started with Custom Functions. For the Deno runtime, environment variables, the key-value store, and OAuth recipes, see Custom Functions Technical Overview.
Use Push from your code when function code should be reviewed, versioned in git, and deployed like the rest of your software. Use New function in the dashboard for a prototype or a one-off that does not belong in a repository.
Code syncs into Transcend only. Edits you make in the dashboard do not write back to your repository.
- A dedicated Sombra™ gateway. Custom Functions run on single-tenant or self-hosted Sombra only.
- An API key with Manage Custom Functions. Create it under Developer Tools → API Keys. Store it as
TRANSCEND_API_KEY. This scope includes View Custom Functions. - When your gateway requires additional authentication, store its Sombra internal key as
SOMBRA_INTERNAL_KEY. Pass it with the--sombraAuthflag. The GitHub Action currently requires this input. Environment-variable values are encrypted by Sombra. Source code is integrity-protected inside a signed token, but remains readable by Transcend's backend for validation. Transcend does not access your repository.
A coding agent can inspect your repository, run the CLI's deterministic initializer, and adapt the generated files to your existing conventions. Give it this prompt:
Use the Transcend Custom Functions skill (https://github.com/transcend-io/tools/blob/main/skills/transcend-custom-functions/SKILL.md) to set up Custom Functions in this repository.
With the Transcend CLI installed, run the interactive initializer:
transcend custom-functions initinit creates or reuses the default transcend/custom-functions/transcend-functions.yml manifest and, by default, offers Deno configuration, target-scoped VS Code settings, the Custom Function coding-agent skill, and credential-free GitHub Actions checks. It previews one transactional plan before writing and requires no Transcend credentials. After setup, use transcend custom-functions new to create a function and transcend custom-functions check to validate the project.
If you prefer not to run init, assemble the repository setup using the sections below.
Every export receives a single argument. Destructure the fields you use. A General function looks like this:
import type { CustomFunction } from '@transcend-io/custom-function-types';
// deno-lint-ignore require-await
export default async function handler({
payload,
}: CustomFunction.GeneralArgument): Promise<void> {
console.info('Payload keys:', Object.keys(payload));
}A data subject request (DSR) function may expose the default export, the enricher export, or both. Configure a test payload for every implemented export. The Admin Dashboard creation flow currently requires both exports to pass their respective tests.
import type { CustomFunction } from '@transcend-io/custom-function-types';
// deno-lint-ignore require-await
export async function enricher({
payload,
}: CustomFunction.EnricherArgument): Promise<void> {
console.info(`Enriching ${payload.requestIdentifier.name}`);
}
// deno-lint-ignore require-await
export default async function customFunction({
payload,
}: CustomFunction.Argument): Promise<void> {
if (payload.type === 'ACCESS') {
console.info(`Processing profile ${payload.extras.profile.id}`);
}
}Install @transcend-io/custom-function-types as a dev dependency for local IntelliSense. The CustomFunction namespace is already available in the dashboard editor, so no import is needed there. See Custom Functions Technical Overview for environment, payload, sdk, and kv.
Save a transcend-functions.yml file in your repository. Each entry maps a function name to a TypeScript source file. Environment values use <<parameters.ENV_NAME>> placeholders so secrets stay out of git.
# transcend-functions.yml
functions:
- name: Update Preferences
code: ./functions/update-preferences.ts
description: Sync preference changes from an external system into the Preference Store
test-payload: ./test-payloads/update-preferences.json
timeout-ms: 30000
env:
TRANSCEND_API_KEY: '<<parameters.TRANSCEND_API_KEY>>'
TRANSCEND_PARTITION: '<<parameters.TRANSCEND_PARTITION>>'
- name: DSR Lookup
code: ./functions/dsr-lookup.ts
type: DSR
description: Fulfill access and erasure requests against the user warehouse
test-payloads:
- payload: ./test-payloads/dsr-lookup.json
payload-type: DATA_POINT
- payload: ./test-payloads/dsr-lookup-enricher.json
payload-type: REQUEST_ENRICHER
allowed-hosts:
- warehouse.internal.example.com
env:
WAREHOUSE_API_KEY: '<<parameters.WAREHOUSE_API_KEY>>'Parameter interpolation is raw text replacement before YAML parsing. Supplied values must remain valid within the surrounding YAML scalar. Values containing quotes, newlines, or YAML syntax require additional care.
Complete function sources and payload files live in the custom-functions-manager examples.
| Field | Required | Notes |
|---|---|---|
name | Yes | Display name. Used as the sync key when no id is set. |
code | Yes | Path to a self-contained TypeScript source file, relative to and contained within the manifest directory. Bundle local runtime imports into this file. |
id | No | Function ID. When set, it becomes the sync key, which lets you rename safely. |
type | No | GENERAL (default) or DSR. |
data-silo-id | Conditional | Omit for a new DSR function and the integration is created for you. Set it to match an existing DSR integration. |
sombra-id | No | Sombra gateway used for creation. An existing function cannot be moved between gateways by changing this value. |
sombra-auth-env | No | Name of the environment variable holding that gateway's internal key. |
test-payloads | No | JSON files the push must pass. Each item has payload (a file path) and optional payload-type. |
test-payload | No | Shorthand for a single test-payloads item. Mutually exclusive with test-payloads. |
allowed-hosts | No | Hosts the function may call. |
timeout-ms | No | Execution timeout in milliseconds. |
allow-third-party-imports | No | Whether the function may import third-party modules. |
env | No | Object map of environment variables. Use <<parameters.ENV_NAME>> placeholders and supply values with --variables. |
description | No | Human-readable description of the function. |
test-payload-type | No | Export used with test-payload: DATA_POINT or REQUEST_ENRICHER. |
On pull requests, install Deno and the CLI, then run the credential-free custom-functions check command. On merges to your main branch, use the transcend-io/custom-functions-manager composite action to push:
name: Sync Transcend Custom Functions
on:
push:
branches: [main]
paths: &custom-function-paths
- 'transcend-functions.yml'
- 'functions/**'
- 'test-payloads/**'
- 'deno.json*'
- 'import_map.json'
pull_request:
paths: *custom-function-paths
jobs:
check-custom-functions:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: denoland/setup-deno@v2
- run: npm install --global @transcend-io/cli
- run: transcend custom-functions check --noInteractive --json --variables=TRANSCEND_API_KEY:placeholder,TRANSCEND_PARTITION:placeholder,WAREHOUSE_API_KEY:placeholder
push-custom-functions:
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: transcend-io/custom-functions-manager@v1
with:
api-key: ${{ secrets.TRANSCEND_API_KEY }}
sombra-auth: ${{ secrets.SOMBRA_INTERNAL_KEY }}
variables: TRANSCEND_API_KEY:${{ secrets.TRANSCEND_PREFERENCES_API_KEY }},TRANSCEND_PARTITION:${{ secrets.TRANSCEND_PARTITION }},WAREHOUSE_API_KEY:${{ secrets.WAREHOUSE_API_KEY }}@v1 is the stable major channel, but it is mutable. Pin an exact cli-version for tighter control. Fully reproducible workflows also require immutable pins for the Action and its nested actions. Pair update-manifest: 'true' with an auto-commit step if you want assigned IDs written back to the repository.
Set sombra-id per manifest entry when functions belong to different gateways. The Action's sombra-auth input is the default internal key. When a gateway uses a different key, set sombra-auth-env on that entry and export the variable on the Action step.
- By
id, when set. The ID is the sync key. You can rename the function freely. A nonexistent ID fails the push. It never silently creates a duplicate. - By exact
name, when noidis set. One match updates it. Zero matches creates it. Multiple functions that share the name fail the push and list the candidate IDs.
Prefer pinning IDs after the first push. Run the CLI with --updateManifest (or enable the Action's update-manifest input and commit the result). Comments and <<parameters.x>> placeholders are preserved. For DSR functions, the assigned data-silo-id is written back the same way.
A new DSR entry that omits data-silo-id gets its DSR integration created as part of the push:
- A Custom Function catalog data silo is created on the entry's Sombra gateway and starts unconfigured.
- The signed code is test-run against that silo. DSR test payloads get
extras.dataSiloinjected automatically, so payload files never hardcode silo IDs. - On a passing test, the function is created and linked. On a failing test, the silo is deleted and the function is rejected.
For the enricher export itself, see Preflight Check: Custom Function.
New, changed, or forced entries with configured test payloads are signed against your gateway and tested before the function revision is saved. Unchanged and metadata-only entries are not retested; changes limited to test payloads require --force. All configured payloads must pass. If any payload fails, each failing case and its logs are printed, the function revision is not saved, and the command exits 1. External side effects performed by the function cannot be rolled back.
- GENERAL payloads are free-form JSON objects.
- DSR payloads use the webhook notification shape. List one payload per export:
payload-type: DATA_POINTfor the default export,payload-type: REQUEST_ENRICHERfor the enricher. A warning is printed when a DSR entry covers only one export. --dryRunnever signs or tests.--skipTestspushes without running payloads. Entries without test payloads push with a warning.
- Install the CLI in the repository:
npm install --global @transcend-io/cli- Push the manifest:
transcend custom-functions push \
--auth="$TRANSCEND_API_KEY" \
--sombraAuth="$SOMBRA_INTERNAL_KEY" \
--variables="TRANSCEND_API_KEY:$TRANSCEND_PREFERENCES_API_KEY,TRANSCEND_PARTITION:$TRANSCEND_PARTITION,WAREHOUSE_API_KEY:$WAREHOUSE_API_KEY"Useful flags:
--dryRunreports what would change without writing.--promote=falseleaves supported new revisions as drafts for review in the dashboard. It does not apply when creating a new DSR function, which is created active. The default is to promote.--forcepushes a new revision when the CLI sees no diff. Required when you only rotated environment variable values, because those values are encrypted at sign time and cannot be compared.--skipTestsskips the test gate.--updateManifestwrites assigned function IDs (and DSR data-silo IDs) back into the manifest.--variables=ENV_NAME:value,...fills<<parameters.ENV_NAME>>placeholders.--transcendUrl=https://api.us.transcend.iois required on US hosting. The default ishttps://api.transcend.io.
transcend custom-functions list prints the functions in your organization.
Pushed functions appear in the Functions list like any other. Version history labels those revisions Pushed via API.
A function is tied to the channel that last updated it. After you push via the CLI, its code is read-only in the dashboard. Further changes have to come from your codebase.
Two behaviors surprise people:
- Rotating only an environment variable value looks like no change. Push with
--force. - Renaming a function without a pinned
idcreates a second function. Run--updateManifestonce so later renames match correctly.
Most teams should use the CLI or Action. The following API calls are selected low-level primitives, not a complete replacement for the CLI workflow.
- Sign the source and execution context against your Sombra customer ingress with
POST /v1/custom/sign, authenticated with the Transcend API key. Include the Sombra internal key only when the selected gateway requires it. The response contains asignedCodeJwt/signedCodeContextJwtpair. A complete API workflow must also handle required Sombra or data-system IDs, test execution, version promotion, DSR data-system creation, and rollback. - Pass those JWTs to
createCustomFunctionorupdateStandaloneCustomFunction. The mutations also still accept a dashboard-styledhEncryptedpayload, which is not the CI path.
The CLI performs these steps, runs test payloads, creates a missing DSR integration, and promotes the new revision. Prefer it unless you are embedding the flow in another toolchain.