Skip to main content

POST /api/crypto/encrypt

Encrypt data, optionally minting a new key.

Encrypts data. If ttag is omitted the key server mints a new key, and the ttag in the response is the only handle to it -- without it the ciphertext cannot ever be decrypted. minted in the response says which happened.

WARNING: ttag: "" is refused rather than treated as omitted. An empty string reaches the wire as the zero-length buffer that means "mint a new key", so a caller who sent one by accident would silently mint a key instead of using theirs. Omit the field entirely to mint deliberately.

The ttag is echoed in the response because the underlying binding bolts it onto the ciphertext buffer as a property rather than returning a pair, and any copy of that buffer loses it.

On a scoped credential the ttag must be in the credential's object list. A mint (no ttag) names no object and is therefore not scope-checked.

Authentication

Send the session id as Authorization: Bearer <session id>, and an RFC 9449 DPoP proof in the DPoP header if your session is bound to a key. The two are required together, not as alternatives — see Authenticating and Proving you hold your key.

This surface sets no cookie

There is no session cookie on any response and none is read on any request. Do not set credentials: "include" on a browser fetch.

Request

FieldTypeRequiredDescription
datastringyesCanonical base64.
ttagstringnoCanonical base64 hidden link. OMIT to mint a new key. Do not send an empty string.
ivstringnoCanonical base64. Optional.

Example

{
"data": "...",
"ttag": "...",
"iv": "..."
}

Responses

200

Encrypted.

FieldTypeRequiredDescription
ciphertextstringyesbase64
ttagstringyesbase64. When minted is true this is the ONLY handle to the new key. Store it before doing anything else.
mintedbooleanyes
ivSuppliedbooleanno
plaintextBytesintegerno
ciphertextBytesintegerno
detailstringyes

Example

{
"ciphertext": "...",
"ttag": "...",
"minted": true,
"ivSupplied": true,
"plaintextBytes": 0,
"ciphertextBytes": 0,
"detail": "..."
}

400

BAD_REQUEST -- data absent or not canonical base64, ttag empty-string, or a malformed iv.

401

The session, or the DPoP proof, or SCOPED_CREDENTIAL_EXPIRED. See the shared Unauthorized description.

403

OBJECT_NOT_IN_CREDENTIAL_SCOPE -- this scoped credential was not minted to cover that ttag. This is not a statement about your rights: whether the principal may reach the object is the key server's answer and it has not been asked; nothing was sent. Or ACCESS_DENIED from the key server itself.

500

A fault rather than a refusal.

OP_FAILED is the catch-all: the key server refused in words that match no classified pattern, so the Gateway cannot say more than that the operation did not happen. Some key server refusals that are conceptually a caller error arrive here rather than as a 4xx, because the wire carries no distinguishing code -- where a route can recognise one from its message it maps it, and each such mapping is documented on the operation.

503

A capacity ceiling or a missing deployment prerequisite. retryable says which.

A scope of session or process names the ceiling that was hit, and the response carries Retry-After.

Error handling

Every failure answers JSON carrying at least code and detail. Match on codedetail is written for a human debugging the call and its wording is not part of the contract. See the error model.

StatusMeaning
400BAD_REQUEST -- data absent or not canonical base64, ttag empty-string, or a malformed iv.
401The session, or the DPoP proof, or SCOPED_CREDENTIAL_EXPIRED. See the shared Unauthorized description.
403OBJECT_NOT_IN_CREDENTIAL_SCOPE -- this scoped credential was not minted to cover that ttag. This is not a statement about your rights: whether the principal may reach the object is the key server's answer and it has not been asked; nothing was sent. Or ACCESS_DENIED from the key server itself.
500A fault rather than a refusal. OP_FAILED is the catch-all: the key server refused in words that match no classified pattern, so the Gateway cannot say more than that the operation did not happen. Some key server refusals that are conceptually a caller error arrive here rather than as a 4xx, because the wire carries no distinguishing code -- where a route can recognise one from its message it maps it, and each such mapping is documented on the operation.
503A capacity ceiling or a missing deployment prerequisite. retryable says which. A scope of session or process names the ceiling that was hit, and the response carries Retry-After.