Kyndex
Getting Started

Quickstart

Register, authenticate, upload an encrypted document, and search it — end to end.

Base URL: https://api.kyndex.co/v1

All endpoints accept and return JSON unless noted otherwise. Authenticated endpoints require a Bearer token in the Authorization header.

SDK Coming Soon

A dedicated Literal client SDK is planned and will handle OPAQUE authentication, client-side encryption, blind index token generation, and key management automatically. Until then, use raw HTTP as documented here.

What Is Literal

Literal is a zero-knowledge, consent-based document verification platform. You upload encrypted documents, and Literal verifies them without ever seeing the plaintext content. Encryption and decryption happen entirely on your device — the server never holds your keys and cannot decrypt your data.

For a deeper look at the cryptographic guarantees, see the Zero-Knowledge Model concept page.

Quickstart Overview

This page gets you from zero to a verified document as fast as possible. Each step links to a dedicated reference page for deeper detail.

StepWhat You'll DoReference
1Register an accountAuthentication
2Log in and get an access tokenAuthentication
3Reserve, encrypt, upload, and search a documentDocument Upload Guide

Prerequisites

  • An OPAQUE client library (e.g. @serenity-kit/opaque for JavaScript) for registration and login
  • A cryptography library for AES-GCM encryption, HMAC-SHA256, and key wrapping
  • An Argon2id implementation for deriving your User Master Key (UMK) from your password

1 — Register

Registration is a two-step OPAQUE handshake. Your client library generates the protocol messages; you send them to the API.

Start registration:

curl -X POST https://api.kyndex.co/v1/auth/opaque/register-start \
  -H 'Content-Type: application/json' \
  -d '{
    "login_bidx": 42,
    "registration_request": "<base64-opaque-message>"
  }'

Finish registration — pass the server's response through your OPAQUE client library, then send the resulting record and generated key material:

curl -X POST https://api.kyndex.co/v1/auth/opaque/register-finish \
  -H 'Content-Type: application/json' \
  -d '{
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "login_bidx": 42,
    "registration_record": "<base64-opaque-record>",
    "email_encrypted": "<base64-aes-256-gcm-ciphertext>",
    "encryption_salt": "<base64-32-bytes>",
    "mlkem_public_key": "<base64-1568-bytes>",
    "x25519_public_key": "<base64-32-bytes>",
    "mlkem_private_encrypted": "<base64-encrypted-private-key>",
    "signing_public_key": "<base64-1984-bytes>",
    "signing_private_encrypted": "<base64-encrypted-signing-key>"
  }'

Neither your password nor your email appear in any request. The login_bidx is derived via an OPRF (Oblivious Pseudorandom Function) evaluation — the client blinds the email, the server evaluates without seeing it, and the client unblinds to produce the bucket index (integer 0-8191). See Authentication for details. The private key fields are encrypted with your UMK (derived client-side from your password) before being sent. See Authentication for full request/response schemas and field descriptions.

2 — Log In

Login is also a two-step OPAQUE handshake, producing an access token and a refresh token.

Start login:

curl -X POST https://api.kyndex.co/v1/auth/opaque/authenticate-start \
  -H 'Content-Type: application/json' \
  -d '{
    "login_bidx": 42,
    "login_request": "<base64-opaque-login-request>"
  }'

Finish login:

curl -X POST https://api.kyndex.co/v1/auth/opaque/authenticate-finish \
  -H 'Content-Type: application/json' \
  -d '{
    "login_session_id": "<uuid-from-step-1>",
    "candidate_index": 0,
    "login_finish": "<base64-opaque-finish-message>",
    "owner_token": "<base64-32-bytes>",
    "user_member_token": "<base64-32-bytes>",
    "revocation_token": "<base64-32-bytes>"
  }'

The response includes an access_token (15-minute TTL) and a refresh_token (7-day, single-use). Use the access token for all subsequent requests:

Authorization: Bearer <access_token>

See Authentication for the full response body, token lifecycle, and refresh flow.

3 — Upload Your First Document

With an access token, follow the reserve → encrypt → create → upload flow.

Reserve a slot:

curl -X POST https://api.kyndex.co/v1/documents/reservations \
  -H 'Authorization: Bearer <access_token>'

This returns a document_id and a commitment_nonce. Incorporate the nonce into your encryption AAD before encrypting.

Encrypt client-side — generate a DEK, encrypt the document with AES-GCM, wrap the DEK twice (once with your key, once with the platform public key), and compute blind index tokens. See the Document Upload Guide for the full breakdown.

Create the document record:

curl -X POST https://api.kyndex.co/v1/documents \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "reservation_id": "<reservation-uuid>",
    "metadata_encrypted": "<base64-encrypted-metadata>",
    "wrapped_dek_umk": "<base64-dek-wrapped-with-your-key>",
    "wrapped_dek_pmk": "<base64-dek-wrapped-with-platform-key>",
    "owner_token": "<base64-owner-token>",
    "doc_token": "<base64-doc-token>",
    "consumer_tokens": [
      { "token": "<base64-blind-index>", "index_type": "doc_type" },
      { "token": "<base64-blind-index>", "index_type": "text_content" }
    ],
    "wrap_ts": 1710288000,
    "size_bucket": 2
  }'

Upload the encrypted bytes:

curl -X PUT https://api.kyndex.co/v1/documents/<document-uuid>/content \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Content-Type: application/octet-stream' \
  -H 'Content-Length: <byte-count>' \
  --data-binary @encrypted-document.bin

Server-side processing (enclave verification, seal generation) starts automatically. Poll GET /v1/documents/<id> until status reaches processed.

4 — Search Your Documents

Once processing completes, search using blind index tokens computed client-side:

curl -X POST https://api.kyndex.co/v1/search \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "token": "<base64-search-token-32-bytes>",
    "scope": "consumer",
    "index_types": ["doc_type", "text_content"]
  }'

The server returns encrypted results — decrypt them client-side. The server never learns what you searched for or what the results contain.

Next Steps

  • Authentication — OPAQUE protocol details, token lifecycle, refresh and logout flows
  • Document Upload Guide — Full encrypt → upload → search walkthrough with processing states and search scopes
  • API Conventions — Error format, rate limiting, public vs. protected routes
  • API Reference — Full endpoint reference with request/response schemas
  • Core Concepts — Zero-knowledge model, document lifecycle, key hierarchy, encrypted search, grants

On this page