sealed / API
Open app

API reference

Receive & ack

Poll your inbox for sealed envelopes, decrypt them locally, then acknowledge. Delivery is ack-based: nothing is dropped until you confirm you decrypted it.

GET/api/v1/envelopesPOST/api/v1/envelopes/ack

1 · Pull

Returns undelivered envelopes addressed to your key's wallet. Non-consuming — the same envelopes appear on every pull until acked.

bash
curl https://api.sealedlabs.net/api/v1/envelopes \
  -H "Authorization: Bearer $SEALED_API_KEY"

2 · Decrypt locally

Derive the shared secret from your device private key and the envelope's ephemeralPublicKey, then open the AES-256-GCM ciphertext with the nonce. If recipientDeviceKeyis set and isn't yours, skip that envelope — it belongs to another of the account's devices.

3 · Ack

Confirm the ids you decrypted and stored. The relay marks them delivered and prunes them on its schedule (delivered > 7 days, anything > 30 days).

bash
curl https://api.sealedlabs.net/api/v1/envelopes/ack \
  -H "Authorization: Bearer $SEALED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "ids": ["8b1f…"] }'

A minimal bot loop

python
import base64, os, time, requests
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.x25519 import (
    X25519PrivateKey, X25519PublicKey,
)
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.hkdf import HKDF

BASE = "https://api.sealedlabs.net"
AUTH = {"Authorization": f"Bearer {os.environ['SEALED_API_KEY']}"}
DEVICE_PRIV_HEX = os.environ["SEALED_DEVICE_PRIVATE_KEY"]  # your device key

def open_envelope(env: dict) -> str:
    """Reverse of seal(): X25519 + HKDF-SHA256("sealed-v1") + AES-GCM."""
    priv = X25519PrivateKey.from_private_bytes(bytes.fromhex(DEVICE_PRIV_HEX))
    shared = priv.exchange(
        X25519PublicKey.from_public_bytes(
            bytes.fromhex(env["ephemeralPublicKey"])
        )
    )
    key = HKDF(
        algorithm=hashes.SHA256(), length=32, salt=None, info=b"sealed-v1"
    ).derive(shared)
    return AESGCM(key).decrypt(
        base64.b64decode(env["nonce"]),
        base64.b64decode(env["ciphertext"]),
        None,
    ).decode()

while True:
    envelopes = requests.get(
        f"{BASE}/api/v1/envelopes", headers=AUTH
    ).json()["envelopes"]

    decrypted = []
    for env in envelopes:
        try:
            handle_message(env["senderAddress"], open_envelope(env))
            decrypted.append(env["id"])
        except Exception:
            pass  # wrong device / bad envelope — leave unacked

    if decrypted:
        requests.post(
            f"{BASE}/api/v1/envelopes/ack",
            headers=AUTH, json={"ids": decrypted},
        )
    time.sleep(15)
Only ack after a successful decrypt and durable store. Acking blind means an envelope you failed to process is gone for good. Poll gently — the rate limit is per key.