Docs menu and this page

On this page

Start: an example for every use case

Each section is one way to hand a secret over, with a command you can run. Set CKRETS to the origin you are calling (production is https://ckrets.ai; a test deployment says so in its banner). The commands need curl and jq.

CKRETS=${CKRETS:-https://ckrets.ai}
# the stage: prod, or a test deployment
curl -s "$CKRETS/v1/limits" | jq -r .stage

Keep API keys out of your agent

Name the recipient by its key id; only that key opens the token. It reveals into an environment variable or a file, so the value reaches the process, not the transcript or mcp.json. (An MCP create_secret takes the value as an argument, so a value you create through MCP is in your context.)

Only the holder of that ckrets key can open it; a leaked link is worthless. The recipient gives you its key id (key_…), never its key.

opens for one ckrets key, by its idlimit free key: 64 KB; it opens while the recipient's key lease lives (7 d at most): for longer, name a public key

POST /v1/secretsto one agent or person
curl -X POST https://ckrets.ai/v1/secrets \
  -H "authorization: Bearer $CKRETS_KEY" \
  -H 'content-type: application/json' \
  -d '{"value":"LAB_7f3a9c","ttl":"1h",
       "audience":{"type":"key","id":"key_"}}'

The recipient reveals with the helper and its own key: npx -y @ckrets/helper@0.9.0 reveal "$TOKEN" --env NAME -- <command>. A key is a lease (24 hours unused, seven days at most); its secrets keep their own TTL.

Pass a secret to a CI job, in any org

The job proves itself with its own OIDC token (GitHub Actions, GitLab CI, CircleCI); the pipeline stores no ckrets key. This narrows exposure; a compromised step in the same job can still read a revealed value. The full two-job workflow is in the guide, section 6.

The job proves itself with its own OIDC token; the pipeline holds no ckrets key.

opens for the deploy.yml workflow of acme/api on main (GitHub Actions)limit free-key limits, counted per repository

POST /v1/secretsto a ci job
curl -X POST https://ckrets.ai/v1/secrets \
  -H "authorization: Bearer $CKRETS_KEY" \
  -H 'content-type: application/json' \
  -d '{"value":"LAB_7f3a9c","ttl":"1h",
       "audience":{"type":"oidc",
         "issuer":"https://token.actions.githubusercontent.com",
         "claims":{"repository":"acme/api",
           "job_workflow_ref":"acme/api/.github/workflows/deploy.yml@refs/heads/main"}}}'

Deliver to a server only its key opens

Name the server by its public key; with clientEncrypted and a key wrapped to its X25519 key (HPKE), ckrets stores ciphertext it cannot read. Making the keys: signing; the formats: client-side encryption.

The server signs its reveal with a key that never leaves it (the signing profile).

opens for the holder of that Ed25519 private key

POST /v1/secretsto a server
curl -X POST https://ckrets.ai/v1/secrets \
  -H 'content-type: application/json' \
  -d '{"value":"LAB_7f3a9c","ttl":"1h",
       "audience":{"type":"publicKey",
         "jwk":{"kty":"OKP","crv":"Ed25519","x":"11qY…"}}}'

Encrypt before you send; we store ciphertext and the key travels in the link (the formats).

opens for whoever has the full link (key after the #)stored as ciphertext only

POST /v1/secretsso we can't read it
curl -X POST https://ckrets.ai/v1/secrets \
  -H 'content-type: application/json' \
  -d '{"value":"v1.AAECAwQFBgcICQoL.VMCrLypv6ihO0X246PtMk4CDM_Wnbl8Oqwc","clientEncrypted":true,"wrappedKey":"fragment.v1","ttl":"1h"}'
# hand over https://ckrets.ai/r/ck_… plus #k=… (the part after # is never sent to us)

The share page decrypts it in the recipient's browser; a recipient who trusts no code of ours decrypts with its own.

Keep credentials out of tickets and chat

Send a link that opens once, to a customer, a contractor or support, instead of pasting the value into a ticket or a chat. The page reveals on a click, so link previews cannot spend it; the status shows when it was collected and whether anyone else tried. Add a passphrase sent another way for a second factor.

Anyone who gets the link opens it once; then it is gone. Send the link over a channel you trust.

opens for anyone with the link, oncelimit 1 KB, 7 d anonymously

POST /v1/secretshand over by link
curl -X POST https://ckrets.ai/v1/secrets \
  -H 'content-type: application/json' \
  -d '{"value":"LAB_7f3a9c","ttl":"1d","reveals":1}'

Send the link one way and the passphrase another; five wrong tries destroy it (ten on a secret made with a key).

opens for the link holder who knows the passphraselimit 7 d anonymously

POST /v1/secretslink plus passphrase
# test values only:
curl -X POST https://ckrets.ai/v1/secrets \
  -H 'content-type: application/json' \
  -d '{"value":"LAB_7f3a9c","ttl":"4h","passphrase":"corvid-lantern-42"}'

# real values: both read from files, never typed
jq -n --rawfile v ./secret.txt \
  --rawfile p ./passphrase.txt \
  '{value:$v, ttl:"4h",
    passphrase:($p | rtrimstr("\n"))}' |
curl https://ckrets.ai/v1/secrets \
  -H 'content-type: application/json' \
  --data-binary @-

Ask a person for a password or code

Your agent sends a link; the person types the value on a page that names who is asking, and only your key can open the answer. It needs a key (POST /v1/requests, then the person opens /q/<id>). This is the web page the MCP specification asks a server to send a user to, instead of asking for a password in the chat. The calls, the limits and the anti-phishing rules: the guide, section 7.

Prove every hand-off

Every create and reveal returns a receipt (a compact JWS) signed with the keys at /.well-known/jwks.json. The sender checks it offline, without calling ckrets, and keeps it as proof the right party collected: how to verify.

Keep it longer, or open it on a date

Every secret lives 7 days unless you ask otherwise: up to 7 days anonymously and up to a year with a free key ("ttl":"365d"). No expiry ("ttl":"never") needs a paid key, which is planned. Riddle, game and dated boxes (open on a date) are made through first-party apps in v1, not through this API directly.

Rotate after a breach planned

POST /v1/batches will send new secrets to many named recipients in one call, with one manifest receipt. Specified in /openapi.yaml; not built yet (the guide, section 8).

Health and card data under contract planned

Classes for health data (HIPAA) and card data (PCI DSS) are planned, under contract. ckrets claims no compliance or certification today. Until then, keep your servers to tokens: a backend of yours that reveals card data is in your PCI DSS scope, and one that reveals health data holds ePHI under HIPAA.


The API

JSON in, JSON out, described in OpenAPI 3.1. Base URL https://ckrets.ai/v1. Every answer carries Ckrets-Version, the dated contract it follows (2026-11-06, the first); send the header to pin your code to a date, send none for the original contract, and a date this server does not serve is 400 version-unknown.

CallWhat it does
POST /v1/secretsStore a value under a policy (ttl, reveals, passphrase, audience, purpose). Returns token, url, manage token, digest and receipt.
GET /v1/secrets/{token}Reveal; the recipient proves its audience. A passphrase is sent with POST. Returns the value and a signed receipt.
GET /v1/secrets/{token}/publicWhat a recipient may see before revealing: the sender, expiry, size, audience. Spends nothing.
GET /v1/secrets/{token}/statusThe creator's view: state, who revealed, attempts after the last reveal. Never the value.
DELETE /v1/secrets/{token}Revoke, with the creator's key or the manage token.
POST /v1/keysMint a free key, no account (below).
GET /v1/limitsYour tier, counters, remaining budget, stamp parameters and the stage (try it).
POST /v1/reportReport a token or URL with a reason; a bearer token is frozen pending review.

Planned (specified in /openapi.yaml, not built yet): POST /v1/batches (many secrets to many named recipients, for rotation after an incident) and POST /v1/me/webhooks (signed alarms). POST /v1/requests (ask a person for a value) is in the beta: above.

Get a free key

No account, no email needed. A key raises every limit and keeps working when the anonymous tier is paused (503). It is a lease: it lasts 24 hours from its last use and seven days at most; long-lived keys are planned.

POST /v1/keysfree key201 Created
curl -X POST https://ckrets.ai/v1/keys \
  -H 'content-type: application/json' -d '{"label":"deploy-bot"}'

# response: keep "key" secret (shown once); give senders "keyId"
{ "key": "ck_live_9mQ2…", "keyId": "key_7TQM4C…", "tier": "free",
  "lease": { "windowEndsAt": "…", "hardEndsAt": "…" } }

Limits

Per tier, enforced per caller, and in every response's headers (RateLimit-Remaining, Ckrets-Tier). The full table: limits.

LimitAnonymousFree keyPaid (planned)
value size1 KB64 KB1 MB
ttl7 d default and max7 d default, 1 year maxno max
reveals1up to 10up to 1,000
creates60 an hour, 300 a day per IP; 240 an hour, 1,200 a day per network1,000 a dayby plan

A CI job creates with its own OIDC token, at free-key limits per repository. Signed agents (RFC 9421, Web Bot Auth) get their own tier, planned: 4 KB, up to 3 reveals, 300 creates an hour.

Errors

Every error is application/problem+json: branch on type, never on title. Each type has a page at /problems/<type> with what to do next; all the types. The first ones you meet:

Two answers carry no problem body; they come from the platform in front of the API. A plain 403 means one address sent too fast (over 300 creates a minute, or 100 requests a minute to one /v1/secrets/… path): wait a minute or two, then slow down. A plain 503 usually means load was shed: after a read, wait 2 seconds and retry; after a write, check first, as for outcome: "unknown" (the rules).