Open source quickstart | Ory

Issue, verify, and revoke an API key with the Ory Talos open-source edition

This guide uses Docker Compose with a SQLite backend, so you need git and Docker installed. Examples use the Ory Talos CLI, with curl as an alternative.

To run the commercial (OEL) edition with Postgres, see the Commercial quickstart instead.

Start the server

Clone the repository to fetch the Docker Compose file, then start the stack:

git clone https://github.com/ory/talos.git

cd talos

docker compose -f docker-compose.oss.yaml up --build -d

This starts Ory Talos with SQLite and Jaeger for tracing (UI at http://localhost:16686). Migrations run automatically. Ory Talos has no built-in web UI; manage keys with the API or CLI. For a web console, see Editions.

The server listens on http://localhost:4420. Wait for it to become healthy:

for i in $(seq 1 30); do

if curl -sf http://localhost:4420/health/alive > /dev/null 2>&1; then

echo "Server is ready"

break

fi

sleep 1

done

Check that it responds:

curl -sf "$TALOS_URL/health/alive" | head -c 200

Issue an API key

Create an API key through the admin surface:

RESPONSE=$(talos keys issue "My first key" \

--actor quickstart-user \

--scopes "read:*,write:*" \

--ttl 168h \

--format json \

-e "$TALOS_URL" 2>/dev/null)

echo "$RESPONSE" | jq .

export API_SECRET=$(echo "$RESPONSE" | jq -er '.secret')

export KEY_ID=$(echo "$RESPONSE" | jq -er '.issued_api_key.key_id')
# Issue a key and capture the response

RESPONSE=$(curl -s -X POST "$TALOS_URL/v2alpha1/admin/issuedApiKeys" \

-H "Content-Type: application/json" \

-d '{

"name": "My first key",

"actor_id": "quickstart-user",

"scopes": ["read:*", "write:*"],

"ttl": "168h"

}')

echo "$RESPONSE" | jq .

# Save the secret and key ID for later steps

export API_SECRET=$(echo "$RESPONSE" | jq -er '.secret')

export KEY_ID=$(echo "$RESPONSE" | jq -er '.issued_api_key.key_id')

The response contains two parts:

Verify the key

Send the secret to the verify endpoint to check that the key is active:

talos keys verify "$API_SECRET" -e "$TALOS_URL"
VERIFY_RESPONSE=$(curl -s -X POST "$TALOS_URL/v2alpha1/admin/apiKeys:verify" \

-H "Content-Type: application/json" \
  
  -d '{"credential":"$API_SECRET"}')

echo "$VERIFY_RESPONSE" | jq .

The response sets is_valid to true and returns the key's metadata (actor, scopes, expiration).

Revoke the key

Revoke the key through the admin surface, using its ID:

talos keys revoke "$KEY_ID" --reason superseded -e "$TALOS_URL"
curl -s -X POST "$TALOS_URL/v2alpha1/admin/apiKeys/${KEY_ID}:revoke" \
  
  -H "Content-Type: application/json" \
  
  -d '{"reason":"REVOCATION_REASON_SUPERSEDED"}'

echo ""

echo "Key revoked"

Verify that the revoked key no longer passes. The --no-cache flag (and the Cache-Control: no-cache header in the curl example) forces a fresh database lookup, bypassing any verification cache:

talos keys verify "$API_SECRET" --no-cache -e "$TALOS_URL" || true

echo "Revocation confirmed"
REVOKE_CHECK=$(curl -s -X POST "$TALOS_URL/v2alpha1/admin/apiKeys:verify" \
  
  -H "Content-Type: application/json" \
  
  -H "Cache-Control: no-cache" \
  
  -d '{"credential":"$API_SECRET"}')

echo "$REVOKE_CHECK" | jq .

# Verify the key is no longer active

if echo "$REVOKE_CHECK" | jq -e '.is_valid == false' > /dev/null 2>&1; then

echo "Revocation confirmed"

else

echo "ERROR: Key should have been revoked"

exit 1
fi

The secret is still cryptographically valid, but verification now fails because the server checks revocation status on a fresh database lookup. When a verification cache is enabled, a revoked key can still pass until its cached entry expires, which is why this step bypasses the cache.

Stop the server

docker compose -f docker-compose.oss.yaml down

To also remove all data volumes:

docker compose -f docker-compose.oss.yaml down -v

Next steps