API Keys

Create, list, rotate, and delete project secret keys via /api/v1/api-keys.

The /api/v1/api-keys group manages the API keys that belong to a project — the secret keys your servers use and the publishable keys your apps ship with. See Authentication for how each key type is used to call the API.

All endpoints in this group live under /api/v1/api-keys and authenticate via the standard auth headers. They are project-scoped: key credentials carry exactly one project, and user sessions act on their first project — there is no explicit project parameter except on create.

The ApiKey object

FieldTypeDescription
idstringUnique key identifier (api_sk_...). Stable across rotation.
namestringHuman-readable label you chose at creation.
projectIdstringThe project this key belongs to.
isPublicbooleantrue for publishable keys, false for secret keys.
prefixstringKey prefix — vh_sk_ for secret keys, vh_pk_ for publishable keys.
endstringThe last 4 characters of the key, for display (vh_sk_...WxYz).
rawKeystring (optional)The full key value. See below for when it is present.
{
  "id": "api_sk_01hxyz",
  "name": "Production server",
  "projectId": "prj_01hxyz",
  "isPublic": false,
  "prefix": "vh_sk_",
  "end": "WxYz"
}

ApiKey vs ApiKeyWithRawKey

Secret keys are hashed before storage — Voidhash keeps only a SHA-256 digest, never the key itself. That means the full secret key value can only exist in two responses:

  • Create (POST /api/v1/api-keys) returns ApiKeyWithRawKey — the object above with rawKey required.
  • Rotate (POST /api/v1/api-keys/:apiKeyId/rotate) also returns ApiKeyWithRawKey with the new key value.

Every other read — list and get by id — returns ApiKey, where rawKey is absent for secret keys. Use prefix and end to render an identifiable placeholder like vh_sk_...WxYz in your own tooling.

Store the raw key immediately

The rawKey in the create and rotate responses is the only time you will ever see the full secret key. It cannot be retrieved later — not by list, not by get. If you lose it, rotate the key to get a new one.

Publishable keys are the exception: they are not secret, so rawKey is included whenever a publishable key appears in list or get responses.

Create a secret key

POST /api/v1/api-keys

Body fieldTypeDescription
namestringLabel for the key (e.g. the service that will use it).
projectIdstringThe project to create the key in.
curl -X POST https://api.voidhash.com/api/v1/api-keys \
  -H "x-api-key: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Production server", "projectId": "prj_01hxyz" }'

Returns ApiKeyWithRawKey:

{
  "id": "api_sk_01hxyz",
  "name": "Production server",
  "projectId": "prj_01hxyz",
  "isPublic": false,
  "prefix": "vh_sk_",
  "end": "WxYz",
  "rawKey": "vh_sk_AbCdEfGhIjKlMnOpQrStUvWxYz"
}
Status_tagWhen
403Api/ActionForbiddenErrorThe caller lacks permission on projectId.
500Api/ApiKeyServiceErrorInternal service failure.

List API keys

GET /api/v1/api-keys

Lists every key in the calling credential's project — publishable keys first, then by creation time. Secret keys come back without rawKey; publishable keys include it.

curl https://api.voidhash.com/api/v1/api-keys \
  -H "x-secret-key: vh_sk_..."

Returns ApiKey[].

Status_tagWhen
403Api/ActionForbiddenErrorNo project in scope, or missing permission.
500Api/ApiKeyServiceErrorInternal service failure.

Get an API key

GET /api/v1/api-keys/:apiKeyId

curl https://api.voidhash.com/api/v1/api-keys/api_sk_01hxyz \
  -H "x-secret-key: vh_sk_..."

Returns a single ApiKey.

Status_tagWhen
403Api/ActionForbiddenErrorThe key belongs to a project the caller cannot access.
404Api/ApiKeyNotFoundErrorNo key with this id exists.
500Api/ApiKeyServiceErrorInternal service failure.

Rotate a secret key

POST /api/v1/api-keys/:apiKeyId/rotate

Replaces the key material in place: a new secret is generated and stored under the same id and name, and the old key stops authenticating immediately. This is the endpoint to reach for when a key leaks.

curl -X POST https://api.voidhash.com/api/v1/api-keys/api_sk_01hxyz/rotate \
  -H "x-api-key: <your-api-key>"

Returns ApiKeyWithRawKey with the new rawKey, prefix, and end — same warning as create: this is the only time you see the new value.

Status_tagWhen
403Api/ActionForbiddenErrorThe caller lacks permission on the key's project.
404Api/ApiKeyNotFoundErrorNo key with this id exists.
500Api/ApiKeyServiceErrorInternal service failure.

Rotating a compromised key

  1. Call rotate on the compromised key. The leaked value is invalid the moment the call succeeds — there is no grace period.
  2. Copy rawKey from the response and update every server, CI secret, and environment variable that used the old value. Anything still holding the old key gets 401 Api/NotAuthenticatedError on its next request.
  3. The id and name are unchanged, so nothing that references the key by id needs to change.

If the key should not exist at all anymore, delete it instead.

Delete an API key

DELETE /api/v1/api-keys/:apiKeyId

Permanently removes the key. Requests using it fail with 401 immediately. Deletion is irreversible — if a service still needs access, prefer rotate.

curl -X DELETE https://api.voidhash.com/api/v1/api-keys/api_sk_01hxyz \
  -H "x-api-key: <your-api-key>"

Returns an empty 200 response on success.

Status_tagWhen
403Api/ActionForbiddenErrorThe caller lacks permission on the key's project.
404Api/ApiKeyNotFoundErrorNo key with this id exists.
500Api/ApiKeyServiceErrorInternal service failure.