Skip to main content
Some services have no ElasticFunnels integration yet: a text-to-speech API, an AI model, your own backend. Save the API key once as a secret, then send it from any backend script as env.secret.NAME. The script never holds the key. env.secret.NAME is a placeholder that is only replaced with the real key inside a request header, on its way to a host you allowed.

Add a secret

  1. Open Settings and pick Backend Scripts Secrets (under Developer).
  2. Click Add secret.
  3. For ElevenLabs, OpenAI, Claude or Gemini, pick it under Start from: it fills in the name, the allowed host and a description, and links to the page where you create the key. For any other service, fill in:
    • Name: capital letters, digits and _, starting with a letter. Example: ELEVENLABS_API_KEY.
    • Value: the API key, at least 8 characters. After you save it, nobody can read it back, you included. The list shows only its last 4 characters.
    • Allowed hosts: the hostnames the key may be sent to, such as api.elevenlabs.io. Use *.example.com to allow every subdomain of example.com (the bare example.com is not included).
    • Description (optional).
  4. Click Save secret.
To replace a key, edit the secret and type the new value. Leave the value empty to keep the current one. Adding a host requires typing the value again, so nobody can redirect a saved key to their own server.

Use it in a script

Click Example next to a saved secret for a ready call that uses it. Services with a preset get their real request; any other secret gets a call to its first allowed host. Put the secret in a request header:
Prefixes work as usual:
Claude needs its version header too:
Gemini takes the key in the x-goog-api-key header (not ?key= in the URL):
Requests time out after 10 seconds, so keep AI answers short (a low max_tokens). The real key is added only when all of these hold:
  • the secret is in a request header (not in the URL or the body),
  • the URL uses https,
  • the URL’s host is in the secret’s allowed hosts.

What the script sees

env.secret.NAME is a random placeholder such as efsec_3f9c..., different on every run. Printing it, saving it with storage.set, returning it in response.json or sending it to another site reveals nothing. If the service echoes the key back in its response (some error messages do), the key is replaced with [redacted] before your script gets the response.

When a request is refused

A refused request is not sent. http.* returns status: 0 and a body that says why:

Good to know

  • Last used in Settings shows when a script last sent the secret.
  • Deleting a secret removes it for good. Scripts using it get env.secret.NAME is not set.
  • Secrets belong to one brand. Other brands cannot use them.
  • Anyone who can edit backend scripts in the brand can use a secret in a request to its allowed hosts, without seeing its value.