Secrets and connections
Credentials stay out of your code. The gateway adds them to the requests that need them.
Stand-ins
Give a secret its hosts, and secrets.NAME in your code is a stand-in: a string like slsec_STRIPE_KEY_1d48… that you put wherever the key goes, including vendor SDKs. The gateway swaps in the real value on the way out, only to those hosts.
PUT /v1/secrets/STRIPE_KEY
{ "value": "sk_live_…", "hosts": ["api.stripe.com"] }
// In the workflow:
const stripe = new Stripe(secrets.STRIPE_KEY);
- Where it swaps
Headers (including inside
Authorization: Basic), the query string, and JSON, form or text bodies up to 1 MiB, over https. - Wrong host
A stand-in sent to any other host stops the request with
403and an audit entry. - Bound to the run
A stand-in is valid only in the run that made it. One pasted into a webhook payload, or copied from another run, stays plain text.
- Echoes
If a host echoes the value back, your code sees
[redacted]. - Unknown names
Reading a secret without hosts, or a misspelled name, throws. Deploys list the names your code reads and warn about missing ones.
Connections
When you need the placement fixed by an admin rather than the code, use a connection: it names the hosts it applies to and an auth template. A workflow declares which connections it uses, and the gateway applies the template to every request to those hosts, overwriting whatever the code set. SDKs that insist on an API key work with a dummy one. The full reference is on Connections.
Connection · stripe
in use- Hosts
api.stripe.com- Header
Authorization- Template
Bearer {{ secret.STRIPE_KEY }}- Secrets
STRIPE_KEY· write-only- Workflows
- refund-check
Last used 21:41:10 · GET api.stripe.com · 200
PUT /v1/connections/twilio
{ "hosts": ["api.twilio.com"],
"template": { "headers": {
"Authorization": "{{ basic(secret.TWILIO_SID, secret.TWILIO_TOKEN) }}" } } }
PUT /v1/workflows/notify/connections
{ "connections": ["twilio"] }
headersHeader name → template.
Host,Content-Lengthand similar headers are reserved.queryQuery parameter → template.
pathThe whole path; expression output is percent-encoded.
body.json · body.formJSON pointer or form field → template. The request must send the matching content type.
Expressions: {{ secret.NAME }}, {{ var.NAME }} (a var), {{ request.method|host|path|query|body }} (body only in headers), ~ to concatenate, | to pipe, and the functions base64 base64url hex urlencode json lower upper trim sha256 hmac_sha256 hmac_sha512 basic now now_ms uuid; see Expressions. When two connections cover one host, pick one per request with the header x-shiftlabs-connection: <name>; the gateway strips it.
Secret and var names are uppercase letters, digits and underscores, starting with a letter. Secrets are write-only: no endpoint returns a value. How the gateway handles them is on the security page.