Seal for sellers

You sell something digital β€” license keys, paid PDFs, premium templates, one-shot data. You already have your own payment processor. Seal is the delivery layer that lets you hand the goods over with the file actually consumed on receipt and proof your platform never had a re-readable copy.

Get an API key Full API docs
Scenario 1

Selling software license keys

You sell a license key for desktop software. Customer pays, your backend generates the key, you want it delivered once β€” to them and only them β€” and removed from your servers afterward.

The flow

Customer ──pays──▢ Stripe Checkout β”‚ β–Ό Stripe webhook ──▢ Your backend β”‚ β”‚ 1. generate license key β”‚ 2. AES-GCM encrypt locally β”‚ 3. POST /v1/seals (ciphertext) β–Ό Seal returns id β”‚ Build link: seal.ai-ministries.com/#/open/{id}!{key} β”‚ β–Ό Email link to customer ──▢ Customer opens once β”‚ β–Ό seal.opened webhook fires back to you β”‚ β–Ό Mark order delivered

The key step (Python)

import os, base64, requests
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

# 1. After Stripe webhook fires, generate the license
license_key = generate_my_license()  # your function

# 2. Encrypt locally β€” server never sees plaintext
key = AESGCM.generate_key(bit_length=256)
nonce = os.urandom(12)
ciphertext = AESGCM(key).encrypt(nonce, license_key.encode(), None)

# 3. POST to Seal
r = requests.post(
    "https://seal.ai-ministries.com/v1/seals",
    headers={"Authorization": f"Bearer {SEAL_API_KEY}"},
    json={
        "ciphertext": base64.b64encode(nonce + ciphertext).decode(),
        "ttl_seconds": 7 * 86400,   # 7 days to open
        "max_reads": 1,             # burn after first open
        "name": "license.txt",
        "mime": "text/plain",
    }
)
sid = r.json()["id"]

# 4. Build the share URL β€” the key goes in the fragment
key_b64url = base64.urlsafe_b64encode(key).rstrip(b"=").decode()
share_url = f"https://seal.ai-ministries.com/#/open/{sid}!{key_b64url}"

# 5. Email it
send_email(customer.email, subject="Your license", body=share_url)

When the customer opens the link, our server fires seal.opened back to your webhook URL so you can mark the order delivered.

Scenario 2

Selling premium PDFs & templates

You sell a $19 paid guide or a Notion template. Buyer should be able to download it a few times within a window (in case they close the tab) but not forever.

The flow

Buyer ──pays $19──▢ Stripe / Lemon Squeezy / Coinbase β”‚ β–Ό Payment webhook ──▢ Your backend β”‚ β”‚ 1. read PDF from disk β”‚ 2. AES-GCM encrypt β”‚ 3. POST /v1/seals β”‚ ttl: 7 days β”‚ max_reads: 3 β–Ό sealed link emailed

What's different

The key step (Node)

import { webcrypto } from 'crypto';
import fs from 'fs';

const pdf = fs.readFileSync('definitive-guide.pdf');

// Encrypt
const key = await webcrypto.subtle.generateKey({name:'AES-GCM',length:256}, true, ['encrypt','decrypt']);
const nonce = webcrypto.getRandomValues(new Uint8Array(12));
const ct = new Uint8Array(await webcrypto.subtle.encrypt({name:'AES-GCM',iv:nonce}, key, pdf));
const blob = new Uint8Array(nonce.length + ct.length);
blob.set(nonce, 0); blob.set(ct, nonce.length);

// Mint
const r = await fetch('https://seal.ai-ministries.com/v1/seals', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${process.env.SEAL_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    ciphertext: Buffer.from(blob).toString('base64'),
    ttl_seconds: 7 * 86400,
    max_reads: 3,
    name: 'definitive-guide.pdf',
    mime: 'application/pdf',
  }),
});
const { id } = await r.json();
const rawKey = new Uint8Array(await webcrypto.subtle.exportKey('raw', key));
const shareUrl = `https://seal.ai-ministries.com/#/open/${id}!${Buffer.from(rawKey).toString('base64url')}`;

// Send to buyer
await sendEmail(buyer.email, shareUrl);
Scenario 3

Anonymous tip / inbound submissions

You run a journalism shop, a security disclosure form, or a "send me a sensitive thing" inbox. You want submissions encrypted before they reach you and gone after you read them.

The flow

Submitter ──fills your form──▢ Browser (your site) β”‚ β”‚ 1. AES-GCM encrypt in browser β”‚ 2. POST your /submit endpoint β”‚ with ciphertext + key fragment β–Ό Your backend β”‚ β”‚ 3. POST /v1/seals (ciphertext only) β”‚ anonymously (no auth header) so β”‚ the seal isn't tied to anyone β–Ό Seal returns id β”‚ β–Ό Email YOU the share link β”‚ β–Ό You read it. It burns.

What's different

Threat model note: our server logs the IP that submitted, the same way any web service does. If you're using this for source protection where the IP itself is dangerous, advise submitters to use Tor or a public network.

Why use Seal as your delivery layer?

Get your API key Read the API docs