DEVELOPERS

Seal your first payload in four calls

Register a key-pair, attest a peer, seal an envelope, collect the receipt. Everything else in this reference is refinement on those four movements. Base URL https://kryptapi.com/v1 · API version 2026-05-01.

QUICKSTART

Four calls, three languages

Sealing happens in your process, not ours. The SDK derives the hop key locally and posts ciphertext; the raw HTTP below shows exactly what crosses the wire if you would rather implement it yourself.

POST /v1/keys · POST /v1/attest · POST /v1/envelopes · GET /v1/envelopes/:id/receipt
# 1 · register a key-pair (public material only — the private key never leaves you)
curl -X POST https://kryptapi.com/v1/keys \
  -H "Authorization: Bearer sk_live_2f8a…" \
  -H "X-Krypt-Api-Version: 2026-05-01" \
  -H "Content-Type: application/json" \
  -d '{
        "label": "ops-dashboard",
        "curve": "P-256",
        "public_jwk": { "kty":"EC","crv":"P-256","x":"8f2a…","y":"d901…" },
        "rotation_days": 90
      }'

# 2 · attest the peer you are about to send to
curl -X POST https://kryptapi.com/v1/attest \
  -H "Authorization: Bearer sk_live_2f8a…" \
  -d '{ "route": "rt_2d55", "peer": "ops-dashboard", "challenge": "9f42a1c7b3d40e11" }'

# 3 · seal and relay — "ct" is ciphertext you produced locally
curl -X POST https://kryptapi.com/v1/envelopes \
  -H "Authorization: Bearer sk_live_2f8a…" \
  -H "Idempotency-Key: 7c4a1f90-2b33-4e55-9a11-0d8e6b2f5c31" \
  -d '{
        "route": "rt_2d55",
        "hdr": { "hop":1, "kid":"key_form_1d90", "alg":"ECDH-ES+A256GCM", "iv":"9pQm1xTf0Ad2" },
        "epk": { "kty":"EC","crv":"P-256","x":"b2c8…","y":"7d19…" },
        "aad": "hop=1;route=rt_2d55;ts=1786553527",
        "ct":  "k9RtQ2b…"
      }'

# -> 201 Created
# { "env":"env_9f42a1c7b3d4c1a7", "state":"relayed", "bytes":41208,
#   "commit":"0x8c31…f19d", "attested":true }

# 4 · collect the receipt (proof of open, hash-chained)
curl https://kryptapi.com/v1/envelopes/env_9f42a1c7b3d4c1a7/receipt \
  -H "Authorization: Bearer sk_live_2f8a…"

Where the sealing happens

Every SDK performs the ECDH agreement and AES-GCM sealing in your process before the HTTP call is made. If a KryptAPI SDK ever asked for your private key material, that would be the bug report of the year — and it is why every SDK is source-available and reproducibly built.

TRY IT LIVE

The same primitives, running in your browser right now

Two real ECDH P-256 key-pairs, one derived AES-256-GCM key, one SHA-256 commit. No network calls — open your devtools and check.

live seal console · webcrypto
Origin — your service
KryptAPI relay (zero-knowledge)
envelope
bytes
iv
origin
dest
time

this is everything we can see

Destination — their service
sealed
sealed → relayed → attested — run the ceremony to generate a signed line.
ENDPOINT REFERENCE

Core routes

Expand any route for its request and response bodies. All requests require Authorization: Bearer and X-Krypt-Api-Version; all mutating requests accept Idempotency-Key.

REQUEST
{
  "route": "rt_2d55",
  "hdr":  { "hop": 1, "kid": "key_form_1d90",
            "alg": "ECDH-ES+A256GCM", "iv": "9pQm1xTf0Ad2" },
  "epk":  { "kty": "EC", "crv": "P-256", "x": "b2c8…", "y": "7d19…" },
  "aad":  "hop=1;route=rt_2d55;ts=1786553527;retain=7y",
  "ct":   "k9RtQ2b…"
}

RESPONSE · 201
{
  "env":       "env_9f42a1c7b3d4c1a7",
  "state":     "relayed",
  "bytes":     41208,
  "attested":  true,
  "commit":    "0x8c31…f19d",
  "parent":    "0x2ab7…0c41",
  "expires_at":"2026-08-15T14:32:07Z"
}
RESPONSE · 200
{
  "env":       "env_9f42a1c7b3d4c1a7",
  "route":     "rt_2d55",
  "hop":       1,
  "state":     "relayed",
  "bytes":     41208,
  "origin_fp": "8f2a…d901",
  "dest_fp":   "5f1c…a740",
  "sealed_at": "2026-08-12T14:32:07Z",
  "opened_at": null,
  "ct":        null
}

Note: "ct" is always null on this route. Ciphertext is
collected exactly once, by the destination, on /open.
REQUEST
{ "attestation": "att_5f1ca740b2c80f55" }

RESPONSE · 200
{
  "env":  "env_9f42a1c7b3d4c1a7",
  "hdr":  { "hop": 1, "kid": "key_form_1d90",
            "alg": "ECDH-ES+A256GCM", "iv": "9pQm1xTf0Ad2" },
  "epk":  { "kty": "EC", "crv": "P-256", "x": "b2c8…", "y": "7d19…" },
  "aad":  "hop=1;route=rt_2d55;ts=1786553527;retain=7y",
  "ct":   "k9RtQ2b…",
  "state":"opened"
}

Decryption happens in your process. A second call returns
409 envelope_already_opened — collection is single-shot.
RESPONSE · 200
{
  "env":          "env_9f42a1c7b3d4c1a7",
  "state":        "opened",
  "sealed_at":    "2026-08-12T14:32:07Z",
  "opened_at":    "2026-08-12T14:32:09Z",
  "opened_by_fp": "5f1c…a740",
  "ct_sha256":    "9ab4c0…7e02",
  "commit":       "0x1e77…d5b8",
  "parent":       "0x8c31…f19d",
  "attestations": [
    { "peer": "web-form.edge",        "sig": "d0f2…771b", "ok": true },
    { "peer": "ops-dashboard.client", "sig": "7c02…e441", "ok": true }
  ]
}
REQUEST
{
  "label":         "ops-dashboard",
  "curve":         "P-256",
  "public_jwk":    { "kty":"EC","crv":"P-256","x":"8f2a…","y":"d901…" },
  "rotation_days": 90,
  "custody":       "hsm"          // "local" | "hsm" | "escrow"
}

RESPONSE · 201
{
  "kid":          "key_ops_8f2a",
  "fingerprint":  "8f2a…d901",
  "curve":        "P-256",
  "custody":      "hsm",
  "created_at":   "2026-08-12T09:04:11Z",
  "rotates_at":   "2026-11-10T09:04:11Z",
  "state":        "active"
}
REQUEST
{
  "new_public_jwk": { "kty":"EC","crv":"P-256","x":"5f1c…","y":"a740…" },
  "quorum":         ["custodian_a","custodian_c"],
  "grace_hours":    72
}

RESPONSE · 200
{
  "kid":            "key_ops_8f2a",
  "rotation":       47,
  "quorum":         "2-of-3",
  "retiring_fp":    "8f2a…d901",
  "incoming_fp":    "5f1c…a740",
  "grace_until":    "2026-08-15T09:04:11Z",
  "commit":         "0x3fa9…aa14",
  "metered_as":     "attestation_event"
}
GET /v1/audit?from=2026-08-01&to=2026-08-12&cursor=…

RESPONSE · 200
{
  "entries": [
    { "ts":"2026-08-12T14:32:07Z", "env":"env_9f42…c1a7",
      "event":"envelope.sealed",  "commit":"0x8c31…f19d",
      "parent":"0x2ab7…0c41" },
    { "ts":"2026-08-12T14:32:09Z", "env":"env_9f42…c1a7",
      "event":"envelope.opened",  "commit":"0x1e77…d5b8",
      "parent":"0x8c31…f19d" }
  ],
  "next_cursor": "cur_9c07f21a",
  "chain_head":  "0x1e77…d5b8",
  "verifier":    "krypt-audit-verify --head 0x1e77…d5b8"
}
REQUEST
{
  "route":     "rt_2d55",
  "peer":      "ops-dashboard",
  "challenge": "9f42a1c7b3d40e11"
}

RESPONSE · 200
{
  "att":              "att_5f1ca740b2c80f55",
  "peer":             "ops-dashboard.client",
  "peer_fp":          "5f1c…a740",
  "signature":        "7c02…e441",
  "counter_challenge":"41ba0d77e5c29a30",
  "suite":            "Ed25519 / P-256 ECDH / AES-256-GCM",
  "expires_in":       300,
  "ok":               true
}
WEBHOOKS & EVENTS

Four events worth waking someone for

Deliveries are signed with your webhook key and retried with exponential backoff for 24 hours. Payloads carry metadata only — an event never contains content.

envelope.sealedat-least-once
{
  "event": "envelope.sealed",
  "env":   "env_9f42a1c7b3d4c1a7",
  "route": "rt_2d55",
  "hop":   1,
  "bytes": 41208,
  "origin_fp": "8f2a…d901",
  "commit": "0x8c31…f19d",
  "ts": "2026-08-12T14:32:07Z"
}
envelope.openedat-least-once
{
  "event": "envelope.opened",
  "env":   "env_9f42a1c7b3d4c1a7",
  "opened_by_fp": "5f1c…a740",
  "latency_ms": 2140,
  "ct_sha256": "9ab4c0…7e02",
  "commit": "0x1e77…d5b8",
  "ts": "2026-08-12T14:32:09Z"
}
key.rotatedexactly-once
{
  "event": "key.rotated",
  "kid":   "key_ops_8f2a",
  "rotation": 47,
  "quorum": "2-of-3",
  "retiring_fp": "8f2a…d901",
  "incoming_fp": "5f1c…a740",
  "grace_until": "2026-08-15T09:04:11Z",
  "ts": "2026-08-12T09:04:11Z"
}
attestation.failedalert
{
  "event":  "attestation.failed",
  "route":  "rt_2d55",
  "peer":   "agent-runtime.client",
  "reason": "policy_digest_mismatch",
  "expected": "pol_intake_v4",
  "observed": "pol_intake_v3",
  "envelope_refused": true,
  "billed": false,
  "ts": "2026-08-12T11:18:44Z"
}
ERROR MODEL

Failures are amber. Crimson is ceremonial.

In KryptAPI's visual and semantic system, crimson belongs to seals, signatures and statements. Errors — in the dashboard, the portal, and every SDK's log formatter — render amber. It sounds cosmetic. It stops people from reading a wax seal as a failure.

error codes · stable across API versions
HTTPCodeMeaning & remedy
400envelope_malformedRequired envelope field missing or the AAD is not a well-formed hop string. Nothing was relayed and nothing was metered.
401attestation_requiredNo valid attestation for this route and peer, or the attestation expired (300s TTL). Re-run POST /v1/attest.
403key_not_authorizedThe key ID is not permitted to originate on this route, or its grace period has closed. Check route policy before rotating again.
409envelope_already_openedCiphertext collection is single-shot by design. Fetch the receipt instead; the envelope's state is terminal.
409idempotency_conflictSame Idempotency-Key, different body. We return the original result rather than guessing which you meant.
413payload_too_largeCiphertext exceeds the route's ceiling (default 32 MB). Chunk into multiple envelopes on the same route ID.
422cipher_suite_unsupportedThe negotiated suite is not in the accepted set. We do not silently downgrade; we refuse.
422envelope_tag_invalidGCM authentication tag mismatch — the envelope was altered or the AAD does not match. Hard failure, never a fallback.
429hop_rate_exceededPer-keypair hop ceiling reached. Retry-After is always present. Ceilings are raised on request, not by tier.
503relay_drainingA relay node is shedding for maintenance. Retry the same Idempotency-Key; the envelope has not been accepted.
error body · shape is identical on every route
{
  "error": {
    "code":      "attestation_required",
    "message":   "No valid attestation for route rt_2d55 and peer ops-dashboard.",
    "route":     "rt_2d55",
    "remedy":    "POST /v1/attest, then retry with the returned att_ id.",
    "request_id":"req_7c4a1f902b33",
    "billed":    false
  }
}
IDEMPOTENCY & LIMITS

Boring guarantees, stated plainly

Idempotency-Key

Send a UUID on any mutating request. We store the first result for 24 hours and replay it verbatim on retry. Same key with a different body returns 409 idempotency_conflict rather than a quiet second send. Retries are never double-metered.

Per-keypair rate ceilings

Ceilings are per key-pair, not per organisation, so one noisy integration cannot starve another. Defaults: 240 seals/sec, 60 attestations/sec, 12 rotations/hour. Retry-After is always present on 429. Raised on request — there is no tier to buy.

Sandbox

Sandbox key-pairs are prefixed sk_test_, route through an isolated relay, and are never metered. Sandbox envelopes expire after 24 hours and cannot address production peers, in either direction.

Versioning

The API version is pinned per request via X-Krypt-Api-Version. Versions are dated, additive within a version, and supported for 24 months after supersession. Breaking changes get a new date and a 90-day overlap.

SDKS

Source-available, reproducibly built

Each SDK ships the sealing primitives, the audit verifier, and a conformance test suite you can run against your own relay.

Node v2.4.0 Python v2.4.0 Go v1.9.2 Rust v0.11.4 CLI krypt v2.4.0

Sandbox traffic is unmetered, permanently. You should be able to break things without reading a statement afterwards.