Actions
Actions let your static app call any HTTPS API without leaking credentials. Wire a Submit button to a Slack webhook, a form to HubSpot, a dashboard to BigQuery — with secrets encrypted at rest, audit logs, rate limits, and OAuth token management all handled for you.
What Actions are
A Deloc Action is a pre-configured HTTP request. You define what the request looks like — method, URL, headers, body — and mark which bits come from the browser and which come from encrypted server-side storage. When your app calls the action, Deloc's edge fills in the template, forwards the request, and returns the upstream response to your app.
The viewer's browser never sees the target URL, the API key, the OAuth token, or anything else sensitive. That means you can add real integrations to a static site without standing up a backend.
Anatomy of an Action
namestringrequiredSlug-style identifier your app uses to invoke it.methodGET | POST | PUT | PATCH | DELETErequiredHTTP method.target_urlstringrequiredFull https:// URL. Supports all three template kinds.header_templateRecord<string,string>optionalOutbound headers. Templated.body_templatestringoptionalRequest body (usually JSON). Templated.allowed_variablesstring[]optionalLowercase variable names the browser may pass. Anything else is rejected.allowed_roles(publisher|admin|viewer)[]optionalWho may invoke. Defaults to publisher+admin.external_id_variablestringoptionalVariable whose value is stamped onto the log row for dedup or audit.rate_limit_per_viewer_per_hournumberoptionalDefault 60. Max 3600.rate_limit_per_app_per_hournumberoptionalDefault 1000. Max 100000.timeout_msnumberoptionalUpstream request timeout. Default 30000.max_response_bytesnumberoptionalMax upstream response size. Default 1MB.credential_namestringoptionalAttach an OAuth credential. Injects ${OAUTH_ACCESS_TOKEN}.Creating an action — end to end
Let's wire a static dashboard so a button POSTs a message to a Slack webhook.
Step 1 — Create the action with a secret placeholder
The ${SLACK_WEBHOOK_URL} placeholder in --target-url is a secret, not a runtime variable. It will be filled in server-side from your encrypted secret store. {message} is a runtime variable the browser passes. {{viewer.email}} is trusted context Deloc sets from the viewer's session.
Step 2 — Set the secret
Omit --value to be prompted with hidden input. The value is encrypted with libsodium secretbox and stored. It is never returned by any API after this point.
Step 3 — Test before shipping
Test invocations are logged with errorType='test' so they don't count toward auto-disable. Check the Slack channel — you should see the message.
Step 4 — Call it from your app
Install the client SDK in your published app's project (same package.json you deploy from):
{slug}.deloc.dev). The Worker uses the viewer's session cookie to authenticate, so you don't need to deal with CORS or bearer tokens in browser code.Template syntax
Every templated field (URL, headers, body) supports three kinds of placeholder.
Runtime variables — {lowercase}
Values the browser passes on invocation. Lowercase only. URL-encoded at runtime when used in a URL. Must appear in --allowed-variables.
Secrets — ${UPPERCASE}
Names must be all uppercase. The value comes from encrypted secret storage and is injected server-side. Viewers never see it.
Trusted context — {{viewer.email}}
Values only Deloc can set. Not controllable by the browser. Supports:
{{viewer.email}}— the signed-in viewer's email{{viewer.id}}— viewer's internal ID{{now}}— ISO-8601 timestamp at invocation{{action.name}}— the action name (useful in logs)
${api_key} (lowercase) is treated as a literal dollar-brace sequence, not secret syntax. Secrets must use ${UPPERCASE_NAMES}. This is an intentional guard so a typo can't accidentally pull in a secret you didn't mean to reference.Secrets
Secrets are per-action. Values never leave the server after they're written.
The platform uses libsodium secretbox (XSalsa20-Poly1305) with a versioned master key. Key rotation is supported and happens transparently — a keyVersion column on each secret row tracks which master key wrapped it. When the master key rotates, old rows are re-encrypted on the next update.
OAuth credentials
Most real APIs need short-lived access tokens, not static API keys. Instead of storing a client secret as a plain action secret and writing token-exchange code yourself, configure an OAuth credential once and let Deloc handle the token mint + cache.
Create a credential
The default grant for machine-to-machine OAuth. Used by Salesforce, Auth0, Okta M2M, most B2B APIs.
Attach to an action
Deloc exchanges the credential for a fresh token (cached until expiry) and substitutes ${OAUTH_ACCESS_TOKEN} on each invocation. Rotate the credential with deloc credentials update.
Invoking from your app
The @deloc/client SDK is a ~3KB shim over fetch. It calls the action on your app's own subdomain (same-origin, no CORS) and authenticates via the viewer's session cookie.
For frameworks without React, or plain HTML:
localhost or a different site. Use deloc actions test for local verification.Testing
Test invocations fire the real upstream request but are marked with errorType='test' in logs — they don't count toward per-viewer or per-app rate limits and they don't trigger auto-disable. Same as the MCP test_action tool.
Logs and audit
Every invocation — real, tested, or failed — is recorded. Each log row has:
- Action name and app slug
- Viewer email (or "publisher"/"admin" for CLI/MCP test invocations)
- External ID (the value of the variable you named in
--external-id-variable, if any) - HTTP status and latency
- Error type (
success,error,test,rate_limit,timeout) - Error message (truncated to 500 chars)
- Timestamp
Max 200 rows per fetch. Logs are also visible in the web dashboard's app detail view.
Rate limits and auto-disable
Every action has two rate limits, both enforced at the Worker:
- Per viewer per hour — default 60. Prevents a single user from hammering the endpoint. Override with
--rate-viewer(1–3600). - Per app per hour — default 1000. Catches runaway loops. Override with
--rate-app(1–100000).
Hitting a rate limit returns a 429 to the browser, logged with errorType='rate_limit'.
Auto-disable kicks in if an action is consistently failing upstream — repeated 4xx/5xx responses flip the action to disabled and log a reason. You'll see the disabled state in deloc actions list or the dashboard. Re-enable with deloc actions enable after fixing the upstream issue.
Tier gating and roadmap
Actions are available on Pro, Pro Unlimited, Team, and Enterprise. Free-tier users see an upgrade screen when they open Actions in the dashboard. No per-action charges — unlimited actions and invocations within your plan's rate-limit ceilings.
Shipped today
- Full HTTP actions with runtime, secret, and trusted-context templates
- Encrypted secrets with rotation
- OAuth credentials (client_credentials, password, jwt_bearer)
- Testing, logs, rate limits, auto-disable
- Generic webhook preset
On the roadmap
- Service-specific presets (Slack, Google Sheets, Linear, HubSpot)
- Scheduled actions (cron-triggered invocations without a viewer)
- Action chaining / workflow builder