Secrets

Manage organization secrets and the attachments that expose them to an app.

Introduction

A secret is stored once for your organization and attached to the apps that need it, so the two halves of this page are separate: the store is org-scoped, and an attachment binds one secret to one app.

An attachment resolves to the environment variable name the value is injected as, which defaults to the secret's own name. That indirection is what lets the same code read HF_TOKEN against a production and a staging credential.

Secrets and plain environment variables share one pod environment and one ceiling of 100 bindings per app. A name already taken by either is rejected with 422, and attaching a secret the app already has returns 409. See Secrets.

List secrets

GETapi.serverless.runware.ai/v1/secrets

Returns secret metadata only. Encrypted values are never returned.

Request

Query

limit

integerint32min: 1max: 100default: 20

Maximum number of items to return.

cursor

string

Opaque pagination cursor returned by a previous call, as nextCursor or, on the operations that offer one, prevCursor.

Response

nextCursor

stringnullable

Cursor for the next page. Null when there are no more items.

data

object[]
Array items6 properties each

id

string (uuid)requiredUUID v4

name

stringrequiredmin: 1max: 128

Organization-scoped secret name. The shape matches EnvironmentVariableName and the secrets.name / deployment_secrets.env_var_name column CHECKs, one rule for the contract and the schema, because attached secrets are intended to be injected as environment variables once ADR-019 in-pod unseal lands. Names the platform sets on the serving container (RUNTIME, DISABLE_NGINX, MLFLOW_MODELS_WORKERS, UVICORN_HOST) are rejected with 422: when injection exists, the deployer appends customer env after its own and kubelet resolves duplicates last-wins, so an accepted collision would silently replace a platform value. Same guard as plain environment variables. Enforced by the server (not expressible as a pattern here).

type

stringrequired

Kind of secret. Only the environment-variable variant (generic) is supported. Image-pull (registry) credentials are consumed by the kubelet before any container starts, so they cannot use the in-pod unseal path (ADR-019) and await their own decision.

Possible values1 value

metadata

objectnullable

Optional opaque metadata associated with the secret.

createdAt

string (date-time)date-time

updatedAt

string (date-time)date-time

Errors

StatusWhen
400The request was malformed and could not be parsed (e.g. invalid JSON). A well-formed request that fails validation returns 422 instead.
401Missing or invalid credentials
422The request was well-formed but semantically invalid (e.g. a missing or out-of-range field). A request that could not be parsed returns 400. The errors array carries one entry per offending field.
500Unexpected server error
503A required service is temporarily unavailable

Create a secret

POSTapi.serverless.runware.ai/v1/secrets

Creates an organization-scoped secret. Returns 409 if the name is already in use, including when a secret of that name is pending_destroy. List only shows active secrets, so a name can appear free while create still conflicts for as long as that row remains. A pending_destroy row is removed, and its name released, by a background sweep once no running worker can still hold the value. There is no deadline on that wait, so a continuously busy app can hold a name for as long as it runs. Recreate-while-deleting may later reuse the pending row with the new value (same name, new ciphertext). An organization whose serverless tenancy is revoked, or has no tenancy receipt at all, returns 403 Forbidden: the per-organization CryptoKey that seals the value is only minted when the tenancy is active. An organization whose tenancy is still being provisioned returns 503 Service Unavailable instead. That state is retryable, not a revocation, and clears once provisioning finishes.

Request

Body

name

stringrequiredmin: 1max: 128

Organization-scoped secret name. The shape matches EnvironmentVariableName and the secrets.name / deployment_secrets.env_var_name column CHECKs, one rule for the contract and the schema, because attached secrets are intended to be injected as environment variables once ADR-019 in-pod unseal lands. Names the platform sets on the serving container (RUNTIME, DISABLE_NGINX, MLFLOW_MODELS_WORKERS, UVICORN_HOST) are rejected with 422: when injection exists, the deployer appends customer env after its own and kubelet resolves duplicates last-wins, so an accepted collision would silently replace a platform value. Same guard as plain environment variables. Enforced by the server (not expressible as a pattern here).

type

stringrequired

Kind of secret. Only the environment-variable variant (generic) is supported. Image-pull (registry) credentials are consumed by the kubelet before any container starts, so they cannot use the in-pod unseal path (ADR-019) and await their own decision.

Allowed values1 value

value

stringrequiredmax: 65536

Plain-text value. Encrypted at rest by the platform. Capped at 64 KiB, Cloud KMS Encrypt's plaintext ceiling, and the reason the design needs no envelope encryption (ADR-019). maxLength is OpenAPI's string-length bound (character count), not a byte limit. Cloud KMS measures UTF-8 bytes, so the server also rejects a value whose UTF-8 encoding exceeds 65536 bytes with 422, a rule this schema cannot express alone (multi-byte characters can pass maxLength and still overrun the KMS limit).

metadata

objectnullable

Response

id

string (uuid)requiredUUID v4

name

stringrequiredmin: 1max: 128

Organization-scoped secret name. The shape matches EnvironmentVariableName and the secrets.name / deployment_secrets.env_var_name column CHECKs, one rule for the contract and the schema, because attached secrets are intended to be injected as environment variables once ADR-019 in-pod unseal lands. Names the platform sets on the serving container (RUNTIME, DISABLE_NGINX, MLFLOW_MODELS_WORKERS, UVICORN_HOST) are rejected with 422: when injection exists, the deployer appends customer env after its own and kubelet resolves duplicates last-wins, so an accepted collision would silently replace a platform value. Same guard as plain environment variables. Enforced by the server (not expressible as a pattern here).

type

stringrequired

Kind of secret. Only the environment-variable variant (generic) is supported. Image-pull (registry) credentials are consumed by the kubelet before any container starts, so they cannot use the in-pod unseal path (ADR-019) and await their own decision.

Possible values1 value

metadata

objectnullable

Optional opaque metadata associated with the secret.

createdAt

string (date-time)date-time

updatedAt

string (date-time)date-time

Errors

StatusWhen
400The request was malformed and could not be parsed (e.g. invalid JSON). A well-formed request that fails validation returns 422 instead.
401Missing or invalid credentials
403Authenticated but not permitted to access this resource
409Resource already exists or the request conflicts with its current state
413The request body exceeds its size limit: 10 MiB on invoke-sync and invoke-async, whose body carries the endpoint's payload, and 1 MiB on the other operations that answer with this response. detail names the limit in bytes.
422The request was well-formed but semantically invalid (e.g. a missing or out-of-range field). A request that could not be parsed returns 400. The errors array carries one entry per offending field.
500Unexpected server error
503A required service is temporarily unavailable

Update a secret

PUTapi.serverless.runware.ai/v1/secrets/{secretName}

Re-encrypts the value under the same name (no rename). Rolls every live deployment that attaches this secret in place so a running worker picks up the new value. If a rollout is already in progress when this commits, this change is not guaranteed to land on it. It reaches the worker on a later redeploy instead. A deployment that is not live picks it up on its next deploy for another reason. An organization whose serverless tenancy is revoked, or has no tenancy receipt at all, returns 403 Forbidden: the per-organization CryptoKey that seals the value is only minted when the tenancy is active. An organization whose tenancy is still being provisioned returns 503 Service Unavailable instead. That state is retryable, not a revocation, and clears once provisioning finishes.

Request

Path

secretName

stringrequiredmin: 1max: 128

Secret name, unique within the authenticated organization.

Body

value

stringrequiredmax: 65536

Plain-text value. Encrypted at rest by the platform. Same 64 KiB Cloud KMS plaintext ceiling and UTF-8 byte check as SecretCreate.value.

metadata

object

Omit to leave the stored metadata unchanged. Send an object (including {}) to replace it.

Response

id

string (uuid)requiredUUID v4

name

stringrequiredmin: 1max: 128

Organization-scoped secret name. The shape matches EnvironmentVariableName and the secrets.name / deployment_secrets.env_var_name column CHECKs, one rule for the contract and the schema, because attached secrets are intended to be injected as environment variables once ADR-019 in-pod unseal lands. Names the platform sets on the serving container (RUNTIME, DISABLE_NGINX, MLFLOW_MODELS_WORKERS, UVICORN_HOST) are rejected with 422: when injection exists, the deployer appends customer env after its own and kubelet resolves duplicates last-wins, so an accepted collision would silently replace a platform value. Same guard as plain environment variables. Enforced by the server (not expressible as a pattern here).

type

stringrequired

Kind of secret. Only the environment-variable variant (generic) is supported. Image-pull (registry) credentials are consumed by the kubelet before any container starts, so they cannot use the in-pod unseal path (ADR-019) and await their own decision.

Possible values1 value

metadata

objectnullable

Optional opaque metadata associated with the secret.

createdAt

string (date-time)date-time

updatedAt

string (date-time)date-time

Errors

StatusWhen
400The request was malformed and could not be parsed (e.g. invalid JSON). A well-formed request that fails validation returns 422 instead.
401Missing or invalid credentials
403Authenticated but not permitted to access this resource
404Resource not found
413The request body exceeds its size limit: 10 MiB on invoke-sync and invoke-async, whose body carries the endpoint's payload, and 1 MiB on the other operations that answer with this response. detail names the limit in bytes.
422The request was well-formed but semantically invalid (e.g. a missing or out-of-range field). A request that could not be parsed returns 400. The errors array carries one entry per offending field.
500Unexpected server error
503A required service is temporarily unavailable

Delete a secret

DELETEapi.serverless.runware.ai/v1/secrets/{secretName}

Soft-deletes a secret: marks the row pending_destroy and bumps revision. This API does not hard-delete the row. Returns 409 while any app still attaches it. Cascade-detach is not performed here. Detach each holder with DELETE .../apps/{id}/secrets/{name} first, which rolls the deployments it names in place. A background sweep removes the row and releases the name once no running worker can still hold the value, rather than assuming every roll a detach started actually landed. A deployment that was not live when detached has nothing to roll until it resumes, so the sweep is the actual backstop, not the detach. While the row remains pending_destroy the name stays reserved, so create may return 409 even though list no longer shows the secret. Retries on an already-pending name are safe when no attachments remain (204). They still return 409 while attached.

Request

Path

secretName

stringrequiredmin: 1max: 128

Secret name, unique within the authenticated organization.

Errors

StatusWhen
401Missing or invalid credentials
404Resource not found
409Resource already exists or the request conflicts with its current state
422The request was well-formed but semantically invalid (e.g. a missing or out-of-range field). A request that could not be parsed returns 400. The errors array carries one entry per offending field.
500Unexpected server error
503A required service is temporarily unavailable

List an app's secrets

GETapi.serverless.runware.ai/v1/apps/{appId}/secrets

Request

Path

appId

stringrequiredmin: 6max: 30

Immutable app identifier, unique among the authenticated organization's live apps.

Query

limit

integerint32min: 1max: 100default: 20

Maximum number of items to return.

cursor

string

Opaque pagination cursor returned by a previous call, as nextCursor or, on the operations that offer one, prevCursor.

Response

nextCursor

stringnullable

Cursor for the next page. Null when there are no more items.

data

object[]
Array items7 properties each

id

string (uuid)requiredUUID v4

name

stringrequiredmin: 1max: 128

Organization-scoped secret name. The shape matches EnvironmentVariableName and the secrets.name / deployment_secrets.env_var_name column CHECKs, one rule for the contract and the schema, because attached secrets are intended to be injected as environment variables once ADR-019 in-pod unseal lands. Names the platform sets on the serving container (RUNTIME, DISABLE_NGINX, MLFLOW_MODELS_WORKERS, UVICORN_HOST) are rejected with 422: when injection exists, the deployer appends customer env after its own and kubelet resolves duplicates last-wins, so an accepted collision would silently replace a platform value. Same guard as plain environment variables. Enforced by the server (not expressible as a pattern here).

type

stringrequired

Kind of secret. Only the environment-variable variant (generic) is supported. Image-pull (registry) credentials are consumed by the kubelet before any container starts, so they cannot use the in-pod unseal path (ADR-019) and await their own decision.

Possible values1 value

metadata

objectnullable

Optional opaque metadata associated with the secret.

createdAt

string (date-time)date-time

updatedAt

string (date-time)date-time

envVarName

stringnullablemin: 1max: 128

Resolved environment variable name when it differs from name. Omitted when the secret name is used. Same reserved-name rules as SecretName when set.

Errors

StatusWhen
400The request was malformed and could not be parsed (e.g. invalid JSON). A well-formed request that fails validation returns 422 instead.
401Missing or invalid credentials
404Resource not found
422The request was well-formed but semantically invalid (e.g. a missing or out-of-range field). A request that could not be parsed returns 400. The errors array carries one entry per offending field.
500Unexpected server error
503A required service is temporarily unavailable

Attach a secret to an app

POSTapi.serverless.runware.ai/v1/apps/{appId}/secrets

Records that an organization secret is attached to an app under a resolved env-var name, and rolls the app's live deployment in place so a running worker picks up the value without waiting for an unrelated deploy. If a rollout is already in progress when this commits, this attach is not guaranteed to land on it. It reaches the worker on a later redeploy instead. A deployment that is not live records the attachment only. The next resume reads the attach set fresh. Returns 409 if the secret is already attached, or if another attach would use the same env-var name. The resolved name (envVarName, or secretName when omitted) must not already exist as a plain environment variable on this app (deployment_configs.key). Both sources use the same pod env namespace, so the server rejects the collision with 422 instead of allowing a last-wins override later. The reverse check applies when setting a plain environment variable. An app holds at most 100 environment bindings in total, plain environment variables plus attached secrets, the same combined ceiling as create and the single-key env-var route. Attaching when the app is already at that limit returns 422. A secret can be attached to at most 25 deployments. Rolling every attached deployment is what an update or detach costs, so the ceiling bounds that cost rather than the app side of the binding. Attaching past it returns 422.

Request

Path

appId

stringrequiredmin: 6max: 30

Immutable app identifier, unique among the authenticated organization's live apps.

Body

secretName

stringrequiredmin: 1max: 128

Organization-scoped secret name. The shape matches EnvironmentVariableName and the secrets.name / deployment_secrets.env_var_name column CHECKs, one rule for the contract and the schema, because attached secrets are intended to be injected as environment variables once ADR-019 in-pod unseal lands. Names the platform sets on the serving container (RUNTIME, DISABLE_NGINX, MLFLOW_MODELS_WORKERS, UVICORN_HOST) are rejected with 422: when injection exists, the deployer appends customer env after its own and kubelet resolves duplicates last-wins, so an accepted collision would silently replace a platform value. Same guard as plain environment variables. Enforced by the server (not expressible as a pattern here).

envVarName

stringnullablemin: 1max: 128

Environment variable name the secret is injected as. Omit or null to use secretName. The server resolves and stores the final name. Same reserved-name rules as SecretName (422 if reserved). The resolved name must also not collide with a plain environment variable key on this app (422).

Errors

StatusWhen
400The request was malformed and could not be parsed (e.g. invalid JSON). A well-formed request that fails validation returns 422 instead.
401Missing or invalid credentials
404Resource not found
409Resource already exists or the request conflicts with its current state
413The request body exceeds its size limit: 10 MiB on invoke-sync and invoke-async, whose body carries the endpoint's payload, and 1 MiB on the other operations that answer with this response. detail names the limit in bytes.
422The request was well-formed but semantically invalid (e.g. a missing or out-of-range field). A request that could not be parsed returns 400. The errors array carries one entry per offending field.
500Unexpected server error
503A required service is temporarily unavailable

Detach a secret from an app

DELETEapi.serverless.runware.ai/v1/apps/{appId}/secrets/{secretName}

Removes the attachment and rolls the app's live deployment in place so a running worker stops receiving the value. If a rollout is already in progress when this commits, this detach is not guaranteed to land on it. The worker stops receiving the value on a later redeploy instead. A deployment that is not live has the removal recorded only. There is nothing to roll until it resumes.

Request

Path

appId

stringrequiredmin: 6max: 30

Immutable app identifier, unique among the authenticated organization's live apps.

secretName

stringrequiredmin: 1max: 128

Secret name, unique within the authenticated organization.

Errors

StatusWhen
401Missing or invalid credentials
404Resource not found
422The request was well-formed but semantically invalid (e.g. a missing or out-of-range field). A request that could not be parsed returns 400. The errors array carries one entry per offending field.
500Unexpected server error
503A required service is temporarily unavailable