Quickstart: the API

The server takes ciphertext. Encrypting is your side of the contract.

Create a secret

Send the envelope, the public salt and the parameters. There is no field for plaintext, a key or a passphrase, by design.

curl -X POST https://api.secretpaste.com/v1/secrets \
  -H 'content-type: application/json' \
  -d '{
    "kind": "text",
    "envelope": "<base64 SPE1 envelope>",
    "salt": "<base64url, 22 chars>",
    "size_bytes": 128,
    "expires_in": 3600,
    "max_views": 1
  }'

What a key may do

A key holds scopes, and it does exactly those: secrets:write to create and revoke, secrets:read for your own list and timelines, receipts:read for who opened what. No key administers the account it belongs to - it cannot mint another key, read billing or change a workspace, because a leaked key that could do those is a leaked account.

With the SDK

The typed client and the crypto package do the parts that are easy to get wrong.

import { createApi } from "@secretpaste/sdk";
import { encryptText, envelopeToJson, toBase64Url } from "@secretpaste/crypto";

const api = createApi({ baseUrl: "https://api.secretpaste.com" });
const sealed = await encryptText("hunter2");

const created = await api.secrets.create({
  kind: "text",
  envelope: envelopeToJson(sealed.envelope),
  salt: toBase64Url(sealed.salt),
  size_bytes: sealed.envelope.length,
  expires_in: 3600,
  max_views: 1,
});

// The key joins the URL here, on this device, and nowhere else.
const url = `${created.url_base}#${toBase64Url(sealed.key)}`;