Skip to content
xashadocs
Tab to a result · Enter to open · Esc to close

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:

javascript
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.

javascript
// 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.

javascript
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.

javascript
// 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.

javascript
// 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().