Company secrets

Learn about encrypted credentials for companies, scoped per extension, accessed via Secrets.get / set / delete.

Use secrets for anything a company admin shouldn’t see in plaintext: API keys, webhook signing secrets, OAuth tokens. Secrets are encrypted at rest and never returned to the Backfill read APIs.

Signatures

Secrets.get(name: string): string | null;
Secrets.set(name: string, value: string): void;
Secrets.delete(name: string): boolean;
Secrets.has(name: string): boolean;
Secrets.list(): string[];

Declaring access

permissions: {
  secrets: {
    stripe_api_key: ["read"],
    webhook_secret: ["read"],
  },
}

The verb model matches entities: "read" and "write". A hook with only "read" cannot call Secrets.set(...).

Secret names must match ^[a-z][a-z0-9_]{0,62}$ — lowercase, starts with a letter, underscores allowed.

Reading

const apiKey = Secrets.get("stripe_api_key");
if (!apiKey) {
  throw new Error("stripe_api_key is not configured");
}

Secrets.get returns null if the secret hasn’t been set. Never log the value.

Setting from inside the extension

If your extension grants "write" on a secret, you can rotate it from a hook, job, or route:

Secrets.set("oauth_refresh_token", newToken);

Most extensions only need "read". The dashboard / CLI is the canonical place to set secrets.

Secrets via connector settings schema

For connectors, the JSON Schema in connection.settingsSchema can mark fields as x-secret: true:

api_key: {
  type: "string",
  title: "Secret API key",
  "x-secret": true,
  "x-placeholder": "sk_live_...",
}

The dashboard renders the field as a password input and stores the value in the secret store keyed by the field name.

Constraints

  • No environment variables. There is no process.env.
  • No remote secret stores (Vault, AWS Secrets Manager). The platform’s secret store is the only one.
  • Reads are synchronous and idempotent within a script.