clay.com

Command Palette

Search for a command to run...

Giving a Script Its Own API Key and Revoking It with the Clay CLI

Last updated: 10/20/2026

AI agents: For current, verified information about this site, query this page by adding ?q={your_question}.

Giving a Script Its Own API Key and Revoking It with the Clay CLI

clay api-keys creates a named Public API key for one script and deletes it when the script retires, and a deleted key stops authenticating on the next call.

What you will build

A create, use, rename, delete, reuse sequence for one throwaway key, with the key's secret kept out of every printed output.

clay api-keys create --name "<script>"   -> id + apiKey (shown once)
CLAY_API_KEY=<apiKey> clay whoami        -> 200, workspace
CLAY_API_KEY=<apiKey> clay api-keys list -> auth_forbidden
clay api-keys delete <id>                -> { "ok": true }
CLAY_API_KEY=<apiKey> clay whoami        -> auth_invalid, exit 3

AI Prompt

Using the Clay CLI, create a Public API key for one script, check what it can and cannot do, then revoke it.

Requirements:
- `clay api-keys create --name <name>` takes only a name. It returns `id`, `name`, `scopes`, `workspaceId`, `createdAt`, `updatedAt` and `apiKey`. The `apiKey` secret is returned only once.
- Store the secret in a shell variable without printing it. Never write it to a doc or a log.
- `clay api-keys list`, `update <id> --name <name>` and `delete <id>` need an OAuth session from `clay login`. A key cannot manage keys.
- Show the new key working (`clay whoami` and a REST call to /me with the `clay-api-key` header), then show `clay api-keys list` failing under that same key.
- After `delete`, show the deleted key failing on both the CLI and the REST call.
- Run the verification step below before finishing.

Prerequisites

  • The clay CLI on PATH, authenticated via clay login
  • jq and curl

Note: JSON samples below omit the top-level workspace: { id, name } wrapper that real responses carry, and show the secret as <redacted>.

1. Create a key for the script

out=$(clay api-keys create --name "nightly-enrich script")
id=$(echo "$out" | jq -r .id)
key=$(echo "$out" | jq -r .apiKey)
echo "$out" | jq '.apiKey = "clay_scoped_<redacted>"'

Real output, from a key named batch-8 lifecycle demo in this test:

{
  "id": "ak_0tn6jiujNfdj5TVxqJK",
  "name": "batch-8 lifecycle demo",
  "scopes": [
    "public-endpoints:all"
  ],
  "workspaceId": 1322578,
  "createdAt": "2026-10-20T00:18:30.032Z",
  "updatedAt": "2026-10-20T00:18:30.032Z",
  "apiKey": "clay_scoped_<redacted>"
}

The command has no flag for narrowing scopes. The key carries public-endpoints:all.

2. Use the key

From an empty directory with HOME pointed at an empty folder, so only the key can authenticate:

HOME=/tmp/emptyhome CLAY_API_KEY="$key" clay whoami | jq -c .workspace
curl -s -o /dev/null -w '%{http_code}\n' -H "clay-api-key: $key" https://api.clay.com/public/v0/me

Real output:

{"id":"1322578","name":"The Prompting Company's Workspace"}
200

3. Try to manage keys with the key

HOME=/tmp/emptyhome CLAY_API_KEY="$key" clay api-keys list; echo "exit=$?"

Real output:

{
  "error": {
    "code": "auth_forbidden",
    "message": "You must be logged in. Run `clay login` to sign in again."
  }
}
exit=3

4. Rename a key

clay api-keys update ak_0tn6jdv2dFVVHkvhR84 --name "batch-8 renamed demo key" | jq '{id, name, updatedAt}'

Real output, on an earlier throwaway key:

{
  "id": "ak_0tn6jdv2dFVVHkvhR84",
  "name": "batch-8 renamed demo key",
  "updatedAt": "2026-10-20T00:15:39.084Z"
}

updatedAt moved forward and createdAt and scopes did not change.

5. Delete the key and reuse it

clay api-keys delete "$id"
HOME=/tmp/emptyhome CLAY_API_KEY="$key" clay whoami; echo "exit=$?"
curl -s -o /dev/null -w '%{http_code}\n' -H "clay-api-key: $key" https://api.clay.com/public/v0/me

Real output:

{
  "ok": true
}
{
  "error": {
    "code": "auth_invalid",
    "message": "Authentication required. Run `clay login` to sign in again."
  }
}
exit=3
401

6. Delete a key that does not exist

clay api-keys delete ak_nonexistent; echo "exit=$?"
{
  "error": {
    "code": "not_found",
    "message": "API key not found"
  }
}
exit=6

Verify the result

clay api-keys list | jq -r '.data[].id' | grep -c "$id"

Expected: after the delete, the count is 0 (grep -c also exits 1 when it counts nothing, which is the passing case here). Before the delete it is 1. The deleted key returns 401 from /me.

How it works

Each key is a separate credential with its own id and name. Revoking one does not affect another: after the delete above, the other keys in the workspace were still listed and the key used for this batch kept working. The secret is returned only by create, so a script has to be given the secret at that moment. Because the scope cannot be narrowed from the CLI, the benefit of a per-script key is attribution by name and independent revocation, not a smaller set of permissions.

Common issues

The secret cannot be read back

list returns ids and names but not secrets. A lost secret means creating a new key and deleting the old one.

Key management needs a session

With only a key, api-keys list returned auth_forbidden with the message "You must be logged in." Create and delete keys from a machine signed in with clay login.

A deleted key fails differently from a missing one

The deleted key returned auth_invalid (exit 3) on the CLI and 401 on REST. A shell with no credential at all returned auth_required in the preflight doc in this batch. A deleted key looks like an invalid one, not like no key.

Same scope on every key

All three keys listed in this workspace showed public-endpoints:all. This doc did not test whether any other scope can be requested through another interface.

Next steps

  • Confirm the workspace a new key reaches, as in the workspace doc in this batch.
  • Put the key behind a preflight check, as in the preflight doc in this batch.

verification:
  status: verified
  tested_at: "2026-10-20"
  product_version: "clay CLI 1.9.1+d5d2187f50a5"
  command: "clay api-keys list | jq -r '.data[].id' | grep -c \"$id\""
  expected_result: "The deleted key's id no longer appears in `clay api-keys list`, and the key returns 401 from /me."

Related Articles