Secrets API

Manage workspace-scoped API keys and credentials used when apps run.

Secret values are never included in list or create/update responses — only masked_value. Use GET /secrets/reveal/{key} when you need the plaintext value (audit-logged).

→ Secrets overview — security model and how apps declare requirements
→ Environment secrets — user-facing setup
→ Extend: declaring secrets — inf.yml schema for app authors
→ Vaults API — group secrets and credentials into named vaults


Scopes

ScopeEndpoints
secrets:readList, get
secrets:read + workspace adminReveal (GET /secrets/reveal/{key})
secrets:write + workspace adminCreate, update, delete

Create, update, delete, and reveal require the workspace admin role (or owner) in addition to the API key scope. Platform admins bypass the role check. List and get are open to any workspace member with secrets:read. A caller without the role gets 403 with code team_role_required.

Create API keys with these scopes at settings → workspace → api keys. See REST overview.


List secrets

GET /secrets or POST /secrets/list

Returns workspace-scoped secrets for the API key's workspace. Credential-managed secrets (scope: internal) and system settings (scope: system) are omitted from list results. When the request has no scope filter, the API applies scope=team. An explicit scope filter overrides it.

Query / body parameters

Uses cursor pagination (cursor, limit, filters). POST /secrets/list accepts the same fields in the JSON body.

Response

FieldTypeDescription
itemsarraySecret objects
next_cursorstringCursor for the next page
has_nextbooleanMore results available
total_itemsnumberTotal matching secrets

Each item:

FieldTypeDescription
idstringSecret ID
keystringEnvironment variable name (e.g. OPENAI_API_KEY)
masked_valuestringMasked preview (never the full value)
descriptionstringUser-visible description
scopestringteam for user-managed secrets
credential_idstringThe credential this secret belongs to. Absent on a plain secret.
created_atstringISO timestamp

Example:

bash
1curl -X POST https://api.inference.sh/secrets/list \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{"limit": 50}'
json
1{2  "items": [3    {4      "id": "sec_abc123",5      "key": "OPENAI_API_KEY",6      "masked_value": "sk-…xxxx",7      "description": "OpenAI API key for GPT models",8      "scope": "team",9      "created_at": "2026-01-15T10:30:00Z"10    }11  ],12  "next_cursor": "",13  "has_next": false,14  "has_previous": false,15  "items_per_page": 1,16  "total_items": 117}

Get secret

GET /secrets/{key}

Requires secrets:read. Returns one secret's metadata for your workspace, with the same fields as a list item, including masked_value but not the plaintext value. Use Reveal secret when you need the decrypted value (audit-logged).

Returns 404 not_found if the key does not exist for your workspace.

Example:

bash
1curl https://api.inference.sh/secrets/OPENAI_API_KEY \2  -H "Authorization: Bearer inf_your_key"
json
1{2  "id": "sec_abc123",3  "key": "OPENAI_API_KEY",4  "masked_value": "sk-…xxxx",5  "description": "OpenAI API key for GPT models",6  "scope": "team",7  "created_at": "2026-01-15T10:30:00Z"8}

belt secrets get calls this endpoint. On deployments that predate the route, the CLI falls back to listing secrets and filtering locally.


Create secret

POST /secrets

Requires secrets:write.

Request body

FieldTypeRequiredDescription
keystringYesEnvironment variable name
valuestringYesSecret value (stored encrypted)
descriptionstringNoShown in the UI and list responses
providerstringNoSlug of the provider this key belongs to (for example openai, acme). The secret becomes that provider's credential. The provider does not have to be one the platform lists.
provider_namestringNoDisplay name of a provider the platform doesn't list. Ignored for a listed provider.
provider_websitestringNoWebsite of a provider the platform doesn't list (acme.com). Its logo is looked up from the domain. Ignored for a listed provider.
connection_scopestringNoWho owns the credential this key activates: user, team, org, or platform. Empty uses the provider default. Requires the matching admin role. Same rules as connection_scope on connect.

When provider is set, the API links the secret to that provider's credential, creating it if none exists. For a provider the platform doesn't list, the credential is an api_key one named provider_name. An api_key credential becomes connected once a key is saved.

When provider is omitted, the API still links the key to a provider's credential if its name is a listed provider's standard API key (for example ARK_API_KEY belongs to ByteDance). Saving such a key from the vault's secrets tab, belt secrets set, or an app's secrets: block makes it that provider's credential all the same. Names that match an OAuth provider's BYOK app fields (such as X_CLIENT_ID) are not inferred this way.

A provider that is not a valid slug (lowercase letters, digits and single hyphens) fails with validation_error, so a key is never saved as a plain secret by mistake. If connection_scope is set, a validation or permission error fails the whole request. If it is omitted and the credential lookup fails, the secret is still created without a link. Omit provider for a plain secret that belongs to no service.

New workspace-scoped secrets are linked to the workspace's default vault.

Example:

bash
1curl -X POST https://api.inference.sh/secrets \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{5    "key": "OPENAI_API_KEY",6    "value": "sk-your-key-here",7    "description": "OpenAI API key"8  }'

Returns the created SecretDTO with masked_value only.

Reserved platform keys

You cannot create team secrets whose key matches an environment variable name reserved for platform settings (SSO, SMTP, object storage, billing, and other deployment configuration). The API returns 400 with code validation_error and a detail such as 'STRIPE_SECRET_KEY' is a reserved platform key. Pick a different name for your app (for example MY_STRIPE_SECRET_KEY) and declare that same name in inf.yml.

Examples of reserved names include STRIPE_SECRET_KEY, SMTP_PASSWORD, TURNSTILE_SECRET_KEY, and STORAGE_ACCESS_KEY_SECRET. The full set matches the legacy environment variable names used for platform settings.

BYOK example with connection scope:

bash
1curl -X POST https://api.inference.sh/secrets \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{5    "key": "OPENAI_API_KEY",6    "value": "sk-your-key-here",7    "provider": "openai",8    "connection_scope": "team",9    "description": "Team OpenAI BYOK key"10  }'

Key for a provider the platform doesn't list:

bash
1curl -X POST https://api.inference.sh/secrets \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{5    "key": "ACME_API_KEY",6    "value": "your-acme-key",7    "provider": "acme",8    "provider_name": "Acme CRM",9    "provider_website": "acme.com",10    "connection_scope": "team"11  }'

Errors:

CodeHTTPWhen
already_exists409A secret with this key already exists for the workspace. To make an existing secret a provider's credential, attach it.
invalid_request400Missing key or malformed body
team_role_required403Caller is not a workspace admin
team_role_required403connection_scope: "org" without org admin
validation_error400An unknown connection_scope, org when the workspace is not part of an organization, or org / platform requested from a workspace other than the organization or platform workspace. detail says which.
forbidden403connection_scope: "platform" without platform admin

Attach a secret to a provider

PUT /secrets/{key}/provider

Requires secrets:write and workspace admin.

Makes an existing secret a provider's credential, with the same effect as creating it with provider. The value and key name stay the same. An empty provider detaches the secret back to a plain secret. The credentials it joins and leaves have their status updated.

Request body

FieldTypeRequiredDescription
providerstringYesProvider slug. Empty string detaches.
provider_namestringNoAs on create
provider_websitestringNoAs on create
connection_scopestringNoAs on create
bash
1curl -X PUT https://api.inference.sh/secrets/ACME_API_KEY/provider \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{ "provider": "acme", "provider_name": "Acme CRM", "provider_website": "acme.com" }'

Returns the SecretDTO. credential_id is set while the secret is attached and absent on a plain secret.

CodeHTTPWhen
not_found404No workspace secret with this key
validation_error400An invalid provider slug, or a secret that is not workspace-scoped

Update secret

PUT /secrets/{key}

Requires secrets:write. Updates the value and/or description for an existing workspace secret.

Request body

FieldTypeRequiredDescription
valuestringYesNew secret value
descriptionstringNoNew description (omit to leave unchanged)

Example:

bash
1curl -X PUT https://api.inference.sh/secrets/OPENAI_API_KEY \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{"value": "sk-rotated-key"}'

Returns 404 not_found if the key does not exist for your workspace.


Reveal secret

GET /secrets/reveal/{key}

Requires secrets:read. Returns the decrypted value. Access is audit-logged (secret.revealed).

Example:

bash
1curl https://api.inference.sh/secrets/reveal/OPENAI_API_KEY \2  -H "Authorization: Bearer inf_your_key"
json
1{2  "key": "OPENAI_API_KEY",3  "value": "sk-your-key-here"4}

Use sparingly in production automations. Prefer injecting secrets at task runtime (the platform resolves requirements when you POST /run) rather than pulling values into your own systems.


Delete secret

DELETE /secrets/{key}

Requires secrets:write.

Example:

bash
1curl -X DELETE https://api.inference.sh/secrets/OPENAI_API_KEY \2  -H "Authorization: Bearer inf_your_key"
json
1{2  "success": true3}

JavaScript SDK

typescript
1import { inference } from '@inferencesh/sdk';23const client = inference({ apiKey: 'inf_your_key' });45const { items } = await client.secrets.list({ limit: 50 });6await client.secrets.create({7  key: 'OPENAI_API_KEY',8  value: 'sk-...',9  description: 'OpenAI',10});11const revealed = await client.secrets.reveal('OPENAI_API_KEY');12await client.secrets.update('OPENAI_API_KEY', { value: 'sk-rotated' });13await client.secrets.delete('OPENAI_API_KEY');

client.secrets.create also accepts provider, provider_name, provider_website and connection_scope. client.secrets.setProvider(key, { provider }) attaches an existing secret.

→ JavaScript SDK · SDK overview


Python SDK

The Python SDK has no client.secrets namespace. Call the REST endpoints directly. inferencesh.types has SecretCreateRequest and CredentialScope for typing the body:

python
1import httpx2from inferencesh.types import CredentialScope, SecretCreateRequest34body: SecretCreateRequest = {5    "key": "OPENAI_API_KEY",6    "value": "sk-...",7    "provider": "openai",8    "connection_scope": CredentialScope.TEAM,9    "description": "Team OpenAI BYOK key",10}11response = httpx.post(12    "https://api.inference.sh/secrets",13    headers={"Authorization": "Bearer inf_your_key"},14    json=body,15).json()

→ Python SDK: generated types


  • Credentials: OAuth and managed service connections (separate from workspace secrets)
  • Extend: secrets — declare secrets: in inf.yml
  • Tasks API — run apps that consume injected secrets
  • Entitlements API — plan limits (API keys, storage, and other resources)

we use cookies

we use cookies to ensure you get the best experience on our website. for more information on how we use cookies, please see our cookie policy.

by clicking "accept", you agree to our use of cookies.
learn more.