@telnyx/edge-runtime) is TypeScript-only today, so other languages use environment variables and the REST APIs where a binding is shown.
Configuration
Keep Secrets Out of Code
Store credentials as secrets — the CLI takes the key and value as positional arguments:[[secrets]] binding in func.toml can also read it through env.SECRETS.get("<handle>"), which telnyx-edge types type-checks against the declared handles. Both surfaces are live at the same time — see Secrets.
Budget for the Platform Timeout
The request timeout is 30 seconds by default and 60 seconds at most — there is nofunc.toml field that raises it. A request that exceeds it is terminated with a 504. Set your own deadlines on outbound calls a few seconds below the platform’s so you fail with a useful error instead (see Time Out and Retry Outbound Calls), and split work that genuinely needs longer. Exact numbers: Limits.
Name Functions for Their URL
The function name becomes the hostname —{func-name}-{org-nickname}.telnyxcompute.com — and new-func registers the function with the platform at scaffold time, so pick the name up front:
Performance
Initialize Once, at Module Scope
A container serves many requests. Module scope runs once per container; the request callback runs per request. Build clients, load config, and compile anything expensive outside the callback:Keep Cold Starts Small
A new container starts when traffic scales up or after a deploy, and the first request it serves waits for everything beforeserver.listen — imports, client construction, config loads. Keep dependencies minimal, and lazy-load heavy libraries used only on rare paths so the common path doesn’t pay for them.
Cache Expensive Reads in KV
Declare a KV namespace infunc.toml and it resolves as a binding on env:
expirationTtlis server-side expiry in whole seconds (≥ 1) and requires@telnyx/edge-runtime≥ 0.2.2 — earlier versions accept the option and silently ignore it.- Keys allow
a-zA-Z0-9-_/=.and forbid colons — writeuser/123, notuser:123.
Put State Where It Belongs
Containers come and go, and concurrent requests can land on different containers — anything kept in process memory is a cache at best. Pick the store by the shape of the data:
Don’t build counters, locks, or rate limiters on KV: it has no transactions or compare-and-swap, and concurrent writers to one key are last-write-wins. That job is exactly what Stateful Actors exist for. Full comparison: Where state lives.
Reliability
Make Handlers Idempotent
Clients retry and webhooks are redelivered, so design handlers where processing the same request twice has the same effect as once. Key side effects on a caller-supplied identifier — a webhook event id, anIdempotency-Key header — and skip work already done. If the duplicate check itself must be race-free, do it inside a Stateful Actor; a check-then-act on KV can race.
Keep the Health Endpoint Fast
The scaffolds answer/health before any other routing:
Catch Everything at the Top of the Handler
Your function is one process. An exception that escapes the request callback — including an unhandled promise rejection — crashes it, drops every in-flight request, and makes the next request pay a cold start. Wrap the whole handler body:Time Out and Retry Outbound Calls
Don’t let a slow upstream ride you into the platform’s 30-second504 — set an explicit deadline on every outbound call:
5xx responses, with exponential backoff, and keep the total budget under the platform timeout:
Security
- Validate input before use — check required fields and types, reject with
400. Nothing between the internet and your handler does it for you. - HTTPS only for outbound calls.
- Never log secret values — and don’t log full request bodies, which may carry PII. Log metadata: method, path, status, duration.
- Authenticate anything that mutates state — your function URL is public. Require a token or shared secret (stored as a secret, checked in the handler) before acting on a request.
Observability
There is no logs command and no metrics dashboard today. What you have is the output ofship, status, and inspect — plus whatever your function emits itself. So emit deliberately:
- Send structured events over HTTP to a sink you control (a log aggregator, your own collector) if you need request-level visibility. There is no surface that shows you
console.logoutput. - Propagate a request id — read
X-Request-IDor generate one, return it in the response, and attach it to every event you emit, so a user-reported failure is findable in your sink.
Next Steps
- Limits — the exact numbers behind timeouts, memory, and payload sizes
- Observability — building your own telemetry with no platform logging surface
- Execution Model — container lifecycle, scaling, and cold starts