# Vault API

> Use when working with HashiCorp Vault REST API — health checks, init/unseal, auth login, KV read/write, policy management, token operations. Covers endpoints, auth methods, curl examples.

- Skill: `kilo-org/vault-api` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add kilo-org/vault-api`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kilo-org/vault-api/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: Kilo-Org (https://skillmd.com/u/kilo-org)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/kilo-org/vault-api

---


# Vault API

## Overview

Vault exposes a RESTful JSON API on port 8200. All requests include `X-Vault-Token` header for authenticated endpoints. Unauthenticated endpoints (health, init) need no token.

**Base URL:** `http://vault.vault:8200` (in-cluster) or `https://vault.kubexa.tech` (external via Gateway).

## API Endpoints

### System

| Method | Endpoint | Description | Auth |
|--------|----------|-------------|------|
| `GET` | `/v1/sys/health` | Cluster health (see status codes below) | No |
| `GET` | `/v1/sys/seal-status` | Seal status | No |
| `PUT` | `/v1/sys/init` | Initialize cluster | No |
| `PUT` | `/v1/sys/unseal` | Unseal (submit key share) | No |
| `GET` | `/v1/sys/leader` | Current leader info | No |

**Health status codes:**

| Code | Meaning |
|------|---------|
| `200` | Active, unsealed |
| `429` | Standby, unsealed |
| `472` | Disaster Recovery secondary (enterprise) |
| `473` | Performance standby (enterprise) |
| `501` | Not initialized |
| `503` | Sealed |

### Auth (Kubernetes)

| Method | Endpoint | Description |
|--------|----------|-------------|
| `POST` | `/v1/auth/kubernetes/login` | Login with service account JWT |

```bash
# Login with K8s SA token
SA_JWT=$(kubectl create token my-sa -n my-ns)
curl -s http://vault.vault:8200/v1/auth/kubernetes/login \
  -d "{\"role\":\"my-role\",\"jwt\":\"$SA_JWT\"}"
# Returns: {"auth":{"client_token":"hvs...","policies":["my-policy"]}}
```

### KV Secrets (v2)

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/v1/{mount}/data/{path}` | Read secret |
| `PUT` | `/v1/{mount}/data/{path}` | Create/update secret |
| `DELETE` | `/v1/{mount}/data/{path}` | Delete latest version |
| `GET` | `/v1/{mount}/metadata/{path}` | Read metadata (versions, timestamps) |
| `POST` | `/v1/{mount}/delete/{path}` | Delete all versions |
| `POST` | `/v1/{mount}/undelete/{path}` | Undelete |

```bash
# Write a secret
curl -s http://vault.vault:8200/v1/secret/data/myapp \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -d '{"data":{"password":"s3cret!"}}'

# Read a secret
curl -s http://vault.vault:8200/v1/secret/data/myapp \
  -H "X-Vault-Token: $VAULT_TOKEN"
# Returns: {"data":{"data":{"password":"s3cret!"},"metadata":{...}}}

# List secrets at a path
curl -s http://vault.vault:8200/v1/secret/metadata/myapp \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  | jq '.data.keys'
```

### KV Secrets (v1)

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/v1/{mount}/{path}` | Read secret |
| `PUT` | `/v1/{mount}/{path}` | Create/update secret |
| `DELETE` | `/v1/{mount}/{path}` | Delete secret |

### Policies

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/v1/sys/policies/acl` | List ACL policies |
| `GET` | `/v1/sys/policies/acl/{name}` | Read policy |
| `PUT` | `/v1/sys/policies/acl/{name}` | Create/update policy |
| `DELETE` | `/v1/sys/policies/acl/{name}` | Delete policy |

```bash
# Create read-only policy for myapp
curl -s http://vault.vault:8200/v1/sys/policies/acl/myapp \
  -X PUT \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -d '{"policy":"path \"secret/data/myapp/*\" {capabilities=[\"read\",\"list\"]}"}'
```

### Token

| Method | Endpoint | Description |
|--------|----------|-------------|
| `POST` | `/v1/auth/token/create` | Create token |
| `POST` | `/v1/auth/token/create-orphan` | Create orphan token |
| `GET` | `/v1/auth/token/lookup-self` | Validate/lookup own token |
| `POST` | `/v1/auth/token/renew-self` | Renew own token |
| `POST` | `/v1/auth/token/revoke-self` | Revoke own token |

### Auth Methods

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/v1/sys/auth` | List enabled auth methods |
| `POST` | `/v1/sys/auth/{type}` | Enable auth method |
| `DELETE` | `/v1/sys/auth/{path}` | Disable auth method |

### Secrets Engines

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/v1/sys/mounts` | List enabled secret engines |
| `POST` | `/v1/sys/mounts/{path}` | Enable secret engine (type: kv-v2, kv, transit, etc.) |
| `DELETE` | `/v1/sys/mounts/{path}` | Disable/delete secret engine |

### Raft

| Method | Endpoint | Description |
|--------|----------|-------------|
| `POST` | `/v1/sys/storage/raft/join` | Join Raft cluster |
| `GET` | `/v1/sys/storage/raft/configuration` | List Raft peers |
| `POST` | `/v1/sys/storage/raft/snapshot` | Take Raft snapshot |

```bash
# Join Raft cluster
curl -s http://127.0.0.1:8200/v1/sys/storage/raft/join \
  -X POST \
  -d '{"leader_api_addr":"http://vault-0.vault-internal:8200"}'

# Take snapshot
curl -s http://vault.vault:8200/v1/sys/storage/raft/snapshot \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -o vault-snapshot.snap
```

## Auth Methods Summary

| Method | Endpoint Mount | Use Case |
|--------|---------------|----------|
| Kubernetes | `/v1/auth/kubernetes/login` | In-cluster pods via SA JWT |
| Token | `/v1/auth/token/create` | Root token, periodic tokens |
| AppRole | `/v1/auth/approle/login` | Machine-to-machine (w/ secretId) |
| Userpass | `/v1/auth/userpass/login` | Human users |
| LDAP | `/v1/auth/ldap/login` | Enterprise directory integration |
| JWT/OIDC | `/v1/auth/jwt/login` | External OIDC providers |
| Cert | `/v1/auth/cert/login` | mTLS client certificates |

## Health Check Examples

```bash
# Quick health
curl -s http://vault.vault:8200/v1/sys/health | jq .initialized

# Detailed status
curl -s http://vault.vault:8200/v1/sys/seal-status | jq '.sealed, .t, .n, .progress'
```

## Common Mistakes

- **KV v2 path includes `data/` prefix.** For KV v2 engine mounted at `secret`, the read path is `/v1/secret/data/myapp`, not `/v1/secret/myapp`. The latter returns a 404.
- **Health endpoint returns non-200 for sealed/standby.** A 503 (sealed) is NOT an error — it's expected after restart. Check `initialized` and `sealed` fields in the response body, not the HTTP status alone.
- **Token in URL is stripped by proxies.** Use `X-Vault-Token` header, not `?token=` query param. Proxies and load balancers may log or strip query params.
- **Kubernetes auth needs SA token with right audience.** The `vault` audience must be configured in the SA or the default token may not be accepted. Use `kubectl create token` with `--audience=vault` for explicit audience.
- **`list` capabilities for metadata listing.** Reading `/v1/secret/metadata/` (to list keys) requires `list` capability at that path, not `read`. Without it, the response is empty.
- **Raft join after unseal.** A sealed node cannot join the Raft cluster. Always unseal before `raft join`. The joining node will sync data from the leader.
- **Snapshot restore requires same cluster size.** Raft snapshots can only be restored to a cluster with the same number of peers. Adding/removing nodes after restore may fail.

