Getting started
Quickstart
Create an encrypted secret, share its link, and retrieve it once. These examples use the same public API as Xasha.
Set up the helper#
Use Node.js 22.18+ or a secure browser context. Save the complete helper from Encryption as encryption.mjs beside your integration module. Add:
import { encryptText, decryptText } from './encryption.mjs'
const api = 'https://api.xasha.site'
const requestOptions = {
cache: 'no-store',
credentials: 'omit',
redirect: 'error',
}Encrypt and create#
Use disposable demo text while trying the API. Only the encrypted envelope and expiry leave your app.
// Run only when the sender chooses Create.
const { envelope, keyFragment } = await encryptText('A disposable demo note')
const createResponse = await fetch(`${api}/secrets`, {
...requestOptions,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ envelope, expiresIn: 3600 }),
signal: AbortSignal.timeout(15000),
})
if (createResponse.status !== 201) {
throw new Error(`Creation failed (${createResponse.status})`)
}
const { id, deleteToken, expiresAt } = await createResponse.json()Build the links#
Share the read link. Keep the separate delete link private. The part after # stays out of HTTP requests.
const shareLink = new URL(`/s/${id}`, 'https://xasha.site')
shareLink.hash = keyFragment
const privateDeleteLink = new URL(`/delete/${id}`, 'https://xasha.site')
privateDeleteLink.hash = deleteToken
// Show links privately with separate copy buttons. Do not log them.Reveal once#
Confirm before retrieving. Do not call the API on page load.
// Run only after an explicit Reveal click. Never automatically retry.
const consumeResponse = await fetch(
`${api}/secrets/${encodeURIComponent(id)}/consume`,
{ ...requestOptions, method: 'POST', signal: AbortSignal.timeout(15000) },
)
if (consumeResponse.status !== 200) {
throw new Error(`Retrieval failed (${consumeResponse.status})`)
}
const { envelope: receivedEnvelope } = await consumeResponse.json()
const revealedText = await decryptText(receivedEnvelope, keyFragment)
// Render as text, not HTML. Keep it in memory only.Delete an unread secret#
Use a second freshly created secret to try deletion. The one above has already been consumed. Confirm the action before sending its token.
// Use an unread secret. Run only after deletion is confirmed.
const deleteResponse = await fetch(
`${api}/secrets/${encodeURIComponent(id)}/delete`,
{
...requestOptions,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ deleteToken }),
signal: AbortSignal.timeout(15000),
},
)
if (deleteResponse.status !== 204) {
throw new Error(`Deletion failed (${deleteResponse.status})`)
}
// 204 has an empty body. Do not call response.json().