What are you trying to do?
Replace my login system
Passwordless authentication with passkeys, email codes, social login, and more.
Accept secure file uploads
Let users upload files that get encrypted and stored in your vault.
Check API availability
Detect whether SparkVault is reachable before starting network-dependent flows.
Use Case: Replace My Login System
Add passwordless verification in 2 steps. Users verify via passkey, email code, SMS, SparkLink, or social login. Your backend verifies the signed JWT and maps the person by SVID. For a full login/session replacement, use the OIDC/BFF flow described in the Identity API docs.
Step 1: Add this to your HTML
Copy this snippet and edit the highlighted values:
<script
src="https://cdn.sparkvault.com/sdk/v1/sparkvault.js"
data-account-id="acc_YOUR_ACCOUNT_ID"
></script>
<script>
SparkVault.products.identity.attach('.login-btn', { // ← your login button selector
onSuccess: async (result) => {
await fetch('/api/auth/login', { // ← your auth endpoint
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: result.token })
});
window.location.href = '/dashboard'; // ← your success redirect
}
});
</script>
Step 2: Verify the token on your backend
Always verify tokens server-side. Never trust the frontend result alone.
Verification code must use https://auth.sparkvault.com for both the JWKS URL
and the expected issuer.
import * as jose from 'jose';
const ACCOUNT_ID = 'acc_YOUR_ACCOUNT_ID'; // ← your account ID
const IDENTITY_URL = 'https://auth.sparkvault.com';
app.post('/api/auth/login', async (req, res) => {
const { token } = req.body;
const jwksUrl = `${IDENTITY_URL}/${ACCOUNT_ID}/.well-known/jwks.json`;
const JWKS = jose.createRemoteJWKSet(new URL(jwksUrl));
const { payload } = await jose.jwtVerify(token, JWKS, {
issuer: `${IDENTITY_URL}/${ACCOUNT_ID}`,
audience: ACCOUNT_ID,
algorithms: ['EdDSA']
});
// Token is valid. SVID is the canonical identity key for your app.
const svid = payload.svid || payload.sub;
const user = await findOrCreateUserBySvid(svid, {
identity: payload.identity,
identityType: payload.identity_type
});
req.session.userId = user.id;
res.json({ success: true });
});
import jwt
from jwt import PyJWKClient
ACCOUNT_ID = 'acc_YOUR_ACCOUNT_ID' # ← your account ID
IDENTITY_URL = 'https://auth.sparkvault.com'
@app.route('/api/auth/login', methods=['POST'])
def login():
token = request.json.get('token')
jwks_url = f"{IDENTITY_URL}/{ACCOUNT_ID}/.well-known/jwks.json"
jwks_client = PyJWKClient(jwks_url)
signing_key = jwks_client.get_signing_key_from_jwt(token)
claims = jwt.decode(token, signing_key.key, algorithms=["EdDSA"],
issuer=f"{IDENTITY_URL}/{ACCOUNT_ID}", audience=ACCOUNT_ID)
# Token is valid. SVID is the canonical identity key for your app.
svid = claims.get('svid') or claims['sub']
user = find_or_create_user_by_svid(
svid,
identity=claims['identity'],
identity_type=claims['identity_type']
)
session['user_id'] = user.id
return jsonify({'success': True})
Configure which auth methods to offer in Identity Product Settings.
The JavaScript SDK simple flow returns a signed verification proof. To have SparkVault own refresh-token
rotation, revocation, and logout, use OIDC with a BFF: exchange codes server-side, keep refresh tokens in
HttpOnly/Secure cookies, validate access JWTs locally with JWKS for normal requests,
introspect only for hard checks, and call /revoke on logout.
Use Case: Accept Secure File Uploads
Let users upload files that get encrypted and stored in your vault.
Uploaded files are encrypted at rest. To access them later, you need a vault member with download access or server-side access via the Ingots API. Public uploaders cannot retrieve files.
Step 1: Enable public upload
Vaults → Edit your vault → Enable Public Ingot Upload.
Step 2: Add this to your HTML
<script
src="https://cdn.sparkvault.com/sdk/v1/sparkvault.js"
data-account-id="acc_YOUR_ACCOUNT_ID"
></script>
<script>
SparkVault.vaults.upload.attach('.upload-btn', { // ← your upload button selector
vaultId: 'vlt_YOUR_VAULT_ID', // ← your vault ID
onSuccess: (result) => {
console.log('Uploaded:', result.ingotId, result.filename);
}
});
</script>
Share the public upload URL from Vault Settings directly, no code required.
Native integration (advanced)
Drive the upload yourself instead of using the SDK drop-zone. Pass a
File from your own button or drop target; the SDK shows only
the secure-upload progress dialog, then closes silently so your app
renders its own result UI. Ideal for chat attachments and similar flows.
// e.g. a chat attachment paperclip
fileInput.addEventListener('change', () => {
SparkVault.vaults.upload({
vaultId: 'vlt_YOUR_VAULT_ID',
file: fileInput.files[0], // skip the SDK drop-zone
folder: 'chat/thread-42', // optional vault sub-path, created if missing
token: vaultAccessToken, // optional VAT: authorize without public upload
hideSuccessScreen: true, // close silently when the upload finishes
onSuccess: (result) => {
// your app renders the "attached" UI
addAttachmentToChat(result.ingotId, result.filename);
},
});
});
file skips the drop-zone, hideSuccessScreen
closes the dialog silently on completion, folder targets a
vault sub-path (find-or-created), and token (a Vault Access
Token) authorizes the upload without public upload being enabled.
Use Case: Check API Availability
Use the SDK health module for lightweight online checks. It calls SparkVault's root health endpoint and returns a structured result instead of throwing on connectivity failures.
const health = await SparkVault.health.check();
if (!health.online) {
console.log('SparkVault is unreachable:', health.error);
}
Installation
CDN (Recommended)
<script
src="https://cdn.sparkvault.com/sdk/v1/sparkvault.js"
data-account-id="acc_YOUR_ACCOUNT_ID"
></script>
npm / yarn
npm install @sparkvault/sdk-js
import SparkVault from '@sparkvault/sdk-js';
const sparkvault = SparkVault.init({
accountId: 'acc_YOUR_ACCOUNT_ID'
});
This SDK is for browser applications (it renders UI). For server-side operations, use the REST API.
React Native Mobile SDK
Use @sparkvault/sdk-mobile for React Native and Expo apps. It includes the same config-driven Identity Product experience as the web popup: email/phone UI derived from enabled delivery methods, configured branding, light/dark mode, SVID-scoped passkeys, and OTP email/SMS/voice.
npm install @sparkvault/sdk-mobile
import { createSparkVaultMobileClient } from '@sparkvault/sdk-mobile';
import { SparkVaultIdentityDialog } from '@sparkvault/sdk-mobile/identity-dialog';
const client = createSparkVaultMobileClient({
tokenStorage,
identityAccountId: 'acc_YOUR_ACCOUNT_ID',
identityBaseUrl: 'https://auth.sparkvault.com',
apiBaseUrl: 'https://api.sparkvault.com/v1'
});
<SparkVaultIdentityDialog
client={client}
visible={visible}
passkeyProvider={passkeyProvider}
onCancel={() => setVisible(false)}
onSuccess={async (result) => {
await client.products.identity.verifyIdentityToken(result.token, { jwks: result.jwks });
const session = await client.account.exchangeIdentityToken(result.token);
// Continue only after local JWT verification and Core exchange succeed.
}}
/>;
Mobile client-side verification protects the app flow before token exchange. Third-party backends must still verify Identity JWTs server-side with the JWKS endpoint and map product records by SVID.
Reference
Identity API Reference
Identity is a first-party product and lives at sparkvault.products.identity.
All methods below are called as sparkvault.products.identity.signIn(...), etc.
signIn(options?)
Open the sign-in popup. Returns a promise that resolves with the verification result.
This is the single programmatic entry point. attach() binds it to element clicks.
Sign-in always runs in a first-party popup on the Identity origin (so cross-domain SSO works);
there is no inline/in-page mode.
const result = await sparkvault.products.identity.signIn({
email: 'user@example.com', // Pre-fill email (optional)
onCancel: () => { ... } // Called if user closes the popup
});
// result.token - Signed JWT (verify on backend!)
// result.identity - Verified email or phone
// result.identityType - 'email', 'phone', or 'social'
// result.svid - Canonical SparkVault ID
// result.sessionId - Managed Identity session ID
Options
| Parameter | Type | Required | Description |
|---|---|---|---|
email |
string | Optional | Pre-fill email address |
phone |
string | Optional | Pre-fill phone (E.164 format: "+14155551234") |
backdropBlur |
boolean | Optional | Override backdrop blur for this popup (uses global config if omitted) |
flow |
"auto" | "popup" | "redirect" | Optional | "auto" (default) and "popup" open the popup; "redirect" navigates the tab to the sign-in page instead (its return leg is handleRedirectResult()) |
onSuccess |
function | Optional | Callback on success |
onError |
function | Optional | Callback on error |
onCancel |
function | Optional | Callback when user closes the popup |
Result
| Field | Type | Description |
|---|---|---|
token |
string | Signed JWT token (Ed25519). Verify on your backend. |
identity |
string | Verified email or phone number |
identityType |
"email" | "phone" | "social" | Type of identity verified |
svid |
string | Canonical SparkVault ID for the person |
sessionId |
string | Managed Identity session ID |
refreshToken |
string | Present only for flows that intentionally return managed-session refresh material |
handleRedirectResult(options?)
Call once on every page load. It redeems a returned one-time, PKCE-bound code (from a
flow: 'redirect' sign-in, or the silent SSO self-heal), never the token itself, and, when a
popup's result was lost (e.g. a mobile browser discarded the backgrounded tab mid-login), it silently
re-mints from the SSO session so the sign-in still completes. It no-ops (resolves null)
otherwise. Wiring it is strongly recommended: it is what makes the popup reliable on mobile. The result
arrives here as the same shape signIn() delivers; verify result.token on your
backend.
// On EVERY page load: redeems a returned code or self-heals a lost popup result.
sparkvault.products.identity.handleRedirectResult({
onSuccess: (result) => createSession(result.token), // same handler as signIn()
}).catch((error) => showError(error));
SparkVault.isRecoveryPending()
Static, side-effect-free, and instance-free: returns true when this page load has a sign-in to
complete: a returned handoff code/error, or a popup whose result was lost. On a high-traffic page where you
defer init() for performance, call this first so you only boot the SDK when
handleRedirectResult() actually has work to do. A visitor who never signed in always gets
false.
// Boot the SDK for the return leg ONLY when a sign-in needs completing.
if (SparkVault.isRecoveryPending()) {
SparkVault.init({ accountId: 'acc_...' })
.products.identity.handleRedirectResult({
onSuccess: (result) => createSession(result.token),
}).catch(showError);
}
attach(selector, options)
Bind verification to element clicks. Returns a cleanup function.
const cleanup = sparkvault.products.identity.attach('.login-btn', {
onSuccess: (result) => { ... },
onCancel: () => { ... }
});
// Later: remove click handlers
cleanup();
close()
Programmatically close the sign-in popup.
sparkvault.products.identity.close();
signInWithFedcm(options?)
One-click sign-in through the browser's native FedCM account chooser. When the person is already
signed into auth.sv (carrying the SSO session), the browser surfaces a "Continue as …" prompt
with no popup, redirect, or typing. Requires a registered clientId in the SDK config.
Resolves with the verified result, or null when the browser has no FedCM support (so you
can fall back to signIn()); throws UserCancelledError when the person dismisses
the chooser. Feature-detect with the static IdentityModule.isFedcmAvailable().
import { IdentityModule } from '@sparkvault/sdk-js';
const result = IdentityModule.isFedcmAvailable()
? await sparkvault.products.identity.signInWithFedcm()
.catch(() => sparkvault.products.identity.signIn())
: await sparkvault.products.identity.signIn();
| Parameter | Type | Required | Description |
|---|---|---|---|
mediation |
'optional' | 'required' | 'silent' | Optional | FedCM mediation mode passed to the browser's credential request |
nonce |
string | Optional | Replay-binding value carried into the token's nonce claim (generated when omitted) |
portalUrl(options?)
Build a deep link into the auth.sv self-service Identity portal, where a signed-in person manages
their SparkVault Identity across every SparkVault-integrated site. Returns an absolute URL on the
configured portalBaseUrl (default https://auth.sv).
// Manage this site (block, notifications, etc.)
window.location.href = sparkvault.products.identity.portalUrl({ section: 'sites' });
// Open the security screen
const url = sparkvault.products.identity.portalUrl({ section: 'security' });
| Parameter | Type | Required | Description |
|---|---|---|---|
section |
string | Optional | Portal screen to open: home | identity | security | sessions | sitesDefault: home |
site |
string | Optional | Focus a specific connected site on the Sites screen (an acc_… account ID). When section is sites and site is omitted, defaults to this SDK's own accountId. Ignored for other sections. |
decodeToken(token)
Decode an identity token's claims without verifying its signature. This is an
unverified, client-side decode: it parses the JWT payload and sanity-checks structure,
exp, iss, aud, and the header alg (rejecting
none and any non-EdDSA algorithm). It does not verify the Ed25519 signature, so its
result must never be trusted for an authorization decision. Verify tokens server-side against
the Identity JWKS endpoint before trusting any claim.
const claims = sparkvault.products.identity.decodeToken(result.token);
console.log(claims.svid, claims.exp);
Authentication Methods
Configure which methods to offer in Identity Product Settings.
Social providers use a full-page OAuth redirect. When the provider returns to your page with #token=...,
products.identity.signIn() and products.identity.attach() consume that fragment once, clear it from the URL,
and call onSuccess with the same result shape as passkey, OTP, and SparkLink.
| Method | Description |
|---|---|
passkey | WebAuthn biometric or security key |
otp_email | 6-digit code via email |
otp_sms | 6-digit code via SMS |
otp_voice | 6-digit code via voice call |
sparklink | One-click sign-in link via email |
social_google | Sign in with Google |
social_apple | Sign in with Apple |
social_microsoft | Sign in with Microsoft |
social_github | Sign in with GitHub |
social_facebook | Sign in with Facebook |
social_linkedin | Sign in with LinkedIn |
Token Claims
{
"iss": "https://auth.sparkvault.com/acc_xxx",
"sub": "ing_019e66a4e27875f2822ede0e4f5d8792",
"aud": "acc_xxx",
"jti": "0192f3a1-7c4e-7b21-b3d4-9a8e6f5c2d10",
"identity": "user@example.com",
"identity_type": "email",
"method": "otp_email",
"verified_at": 1703977200,
"svid": "ing_019e66a4e27875f2822ede0e4f5d8792",
"session_id": "sess_abc123",
"iat": 1703977200,
"exp": 1703977500
}
Simple tokens expire after 5 minutes by default (exp = iat + 300).
Optional claims (nonce, amr, acr, and action_hash)
appear only when the flow binds them.
Upload API Reference
upload(options)
Open the upload widget. Returns a promise that resolves when upload completes.
const result = await sparkvault.vaults.upload({
vaultId: 'vlt_abc123',
target: '#container', // Render inline instead of modal (optional)
onProgress: (p) => console.log(p.percentage + '%')
});
// result.ingotId - Uploaded file ID
// result.filename - Original filename
// result.sizeBytes - File size
Options
| Parameter | Type | Required | Description |
|---|---|---|---|
vaultId |
string | Required | Vault ID to upload to (public upload must be enabled unless token is provided) |
target |
HTMLElement | string | Optional | CSS selector or element for inline rendering |
file |
File | Optional | Upload this File directly, skipping the SDK drop-zone. Your app owns file selection and the SDK shows only the secure-upload progress dialog |
folder |
string | Optional | Slash-delimited folder path within the vault (e.g. Accounts/001xx); missing path segments are find-or-created. Omitted means the vault root. |
token |
string | Optional | Vault Access Token (VAT). When provided, the VAT authorizes the upload; no public-upload flag required. When omitted, the public upload flow is used. |
backdropBlur |
boolean | Optional | Blur background in dialog mode (default: true) |
hideUploadAnother |
boolean | Optional | Hide "Upload Another File" button on success (for single-file flows) |
hideSuccessScreen |
boolean | Optional | Skip the success screen entirely: on completion the SDK fires onSuccess and closes the dialog, so your app renders its own post-upload UI |
onSuccess |
function | Optional | Callback on success |
onError |
function | Optional | Callback on error |
onCancel |
function | Optional | Callback when user cancels |
onProgress |
function | Optional | Callback for progress updates |
Result
| Field | Type | Description |
|---|---|---|
ingotId |
string | Unique identifier for the uploaded file |
vaultId |
string | Vault the file was uploaded to |
filename |
string | Original filename |
sizeBytes |
number | File size in bytes |
uploadTime |
string | Upload timestamp (ISO 8601) |
Progress callback
{
bytesUploaded: 52428800,
bytesTotal: 104857600,
percentage: 50,
phase: 'uploading' // 'uploading' | 'ceremony' | 'complete'
}
upload.attach(selector, options)
Bind upload to element clicks. Returns a cleanup function.
const cleanup = sparkvault.vaults.upload.attach('.upload-btn', {
vaultId: 'vlt_abc123',
onSuccess: (result) => { ... }
});
cleanup(); // Remove handlers
upload.close()
Programmatically close the upload widget.
upload.pop(options) / upload.render(options)
Aliases on the callable: pop() opens the upload widget as a dialog, equivalent to
calling upload(options), and render() renders it inline, equivalent to
upload({ ...options, target }).
Vaults API Reference
The vaults module is the control plane: create vaults, unseal them for
data-plane access, and manage their sharing and upload configuration. The browser upload
widget (vaults.upload, documented above) also lives here.
create() returns the Vault Master Key (vmk) exactly once. It cannot
be recovered. Store it securely. Unsealing, deleting, and enabling sharing all require it.
// Create a vault. Save the VMK!
const vault = await sparkvault.vaults.create({ name: 'My Vault' });
// Unseal to get a short-lived Vault Access Token (VAT).
// The returned object is passed to ingots/folders calls.
const unsealed = await sparkvault.vaults.unseal(vault.id, vault.vmk);
// Inspect, rename, seal
const detail = await sparkvault.vaults.get(vault.id);
await sparkvault.vaults.update(vault.id, { name: 'Renamed' });
await sparkvault.vaults.seal(vault.id);
// List vaults — paginated: returns a page { vaults, nextCursor }.
// Follow nextCursor to fetch subsequent pages.
const page = await sparkvault.vaults.list();
page.vaults.forEach((v) => console.log(v.name));
// Delete requires the vault's exact name as confirmation (sent as the
// X-Confirm-Name header, case-sensitive) — not the VMK.
await sparkvault.vaults.delete(vault.id, vault.name);
Sharing
const config = await sparkvault.vaults.getSharingConfig(vault.id);
await sparkvault.vaults.enableSharing(vault.id, vault.vmk, {
allIngotsPublic: false,
sessionLengthSeconds: 3600,
});
await sparkvault.vaults.updateSharingConfig(vault.id, { sessionLengthSeconds: 7200 });
await sparkvault.vaults.disableSharing(vault.id);
Upload configuration & access-log retention
const upload = await sparkvault.vaults.getUploadConfig(vault.id);
await sparkvault.vaults.enableUploadPortal(vault.id, vault.vmk);
await sparkvault.vaults.disableUploadPortal(vault.id);
await sparkvault.vaults.enableUploadWidget(vault.id, vault.vmk);
await sparkvault.vaults.disableUploadWidget(vault.id);
await sparkvault.vaults.updateUploadConfig(vault.id, {
maxSizeBytes: 1048576,
notificationEmail: 'ops@example.com',
});
const retention = await sparkvault.vaults.getAccessLogRetention(vault.id);
await sparkvault.vaults.updateAccessLogRetention(vault.id, 7776000);
Ingots API Reference
The ingots module operates on the contents of an unsealed vault.
Pass the object returned by vaults.unseal(...) as the first argument. It
carries the short-lived Vault Access Token.
createUpload() and createDownloadLink() are discrete primitives that
return a Forge URL. upload() and download() are the high-level flows:
they drive the resumable transfer end-to-end and verify server-side finalization.
const unsealed = await sparkvault.vaults.unseal(vault.id, vault.vmk);
// High-level upload: creates the session, streams over TUS, verifies finalized.
const ingot = await sparkvault.ingots.upload(unsealed, {
file: myFile,
name: 'document.pdf',
onProgress: (uploaded, total) => console.log(uploaded / total),
});
// High-level download: fetches a fresh signed link and returns a Blob.
const blob = await sparkvault.ingots.download(unsealed, ingot.id);
// Discrete primitives: drive the transfer yourself.
const session = await sparkvault.ingots.createUpload({
vault: unsealed, name: 'big.zip', contentType: 'application/zip', sizeBytes: file.size,
});
const link = await sparkvault.ingots.createDownloadLink(unsealed, ingot.id);
Manage & organize
await sparkvault.ingots.rename(unsealed, ingot.id, 'renamed.pdf');
await sparkvault.ingots.move(unsealed, ingot.id, 'fld_xyz'); // null = vault root
await sparkvault.ingots.replace(unsealed, ingot.id, { file: newFile });
await sparkvault.ingots.delete(unsealed, ingot.id);
const meta = await sparkvault.ingots.get(unsealed, ingot.id);
const { ingots } = await sparkvault.ingots.list(unsealed);
const found = await sparkvault.ingots.search(unsealed, { q: 'invoice' });
const contents = await sparkvault.ingots.listContents(unsealed, { folderId: 'fld_xyz' });
list(), search(), listContents(), and
getAccessLogs() accept { limit, cursor } pagination and return a
nextCursor. list() and listContents() also accept
sortBy (name | size | content_type |
created | access_count) and sortOrder
(asc | desc); search() also accepts
fileType, minSize, maxSize, and sortOrder.
Sharing & access logs
await sparkvault.ingots.updateSharingConfig(unsealed, ingot.id, { shared: true, visibility: 'invite_only' });
const sharing = await sparkvault.ingots.getSharingConfig(unsealed, ingot.id);
// Optional 4th arg: invite lifetime (omit to inherit the link's existing expiry)
const invite = await sparkvault.ingots.addInvite(unsealed, ingot.id, 'user@example.com', { expiresInSeconds: 86400 });
await sparkvault.ingots.revokeInvite(unsealed, ingot.id, invite.id);
const { logs } = await sparkvault.ingots.getAccessLogs(unsealed, ingot.id, { limit: 100 });
Folders API Reference
The folders module organizes ingots within an unsealed vault. The flat
list returned by list() is enough to build the folder tree client-side.
const unsealed = await sparkvault.vaults.unseal(vault.id, vault.vmk);
const folders = await sparkvault.folders.list(unsealed);
const folder = await sparkvault.folders.create(unsealed, { name: 'Invoices' });
const detail = await sparkvault.folders.get(unsealed, folder.id);
const trail = await sparkvault.folders.getBreadcrumb(unsealed, folder.id);
await sparkvault.folders.update(unsealed, folder.id, { name: 'Renamed' });
await sparkvault.folders.move(unsealed, folder.id, 'fld_parent'); // null = vault root
await sparkvault.folders.togglePin(unsealed, folder.id, true);
await sparkvault.folders.moveIngot(unsealed, 'ing_abc', folder.id); // null = vault root
await sparkvault.folders.delete(unsealed, folder.id);
Sparks API Reference
Sparks are end-to-end encrypted, read-once ephemeral secrets scoped to your account (no
vault or VAT required). Reading a spark with get() burns it.
const spark = await sparkvault.sparks.create({
payload: 'top secret',
ttlMinutes: 60, // defaults to 1440 (24h)
contentType: 'text/plain', // optional MIME type (base64 payload for binary types)
filename: 'note.txt', // optional, for file payloads
});
// Kindling groups related sparks into a family
const first = await sparkvault.sparks.create({ payload: 'one', withKindling: true });
const sibling = await sparkvault.sparks.create({ payload: 'two', kindling: first.kindling });
const { sparks } = await sparkvault.sparks.list(); // filters: { kindling, status, limit, cursor }
const revealed = await sparkvault.sparks.get(spark.id); // burns the spark
await sparkvault.sparks.delete(spark.id);
Public sharing (SparkLink)
// Share via a public x.sv link
const share = await sparkvault.sparks.share(spark.id, { visibility: 'public' });
console.log(share.shareUrl); // https://x.sv/...
const status = await sparkvault.sparks.getShare(spark.id);
await sparkvault.sparks.unshare(spark.id);
Entropy API Reference
Generate cryptographically strong random values from the SparkVault hardware-backed entropy service.
// Formatted output: hex | base64 | base64url | alphanumeric | alphanumeric-mixed | password | numeric | uuid | bytes
const { value } = await sparkvault.entropy.generate({ bytes: 32, format: 'hex' });
// Raw bytes as a Uint8Array
const key = await sparkvault.entropy.generateBytes(32);
bytes must be an integer from 1 to 1024 (a ValidationError is thrown
otherwise). For format: 'bytes' the value is a number array.
Notifications API Reference
The signed-in user's metadata-only notification inbox. The recipient is always derived from the
session token, so every call reads or mutates only the caller's own inbox. Rows are locked
pointers: the content lives behind each row's SparkLink (sparklink_code), never in
the row itself. Requires an access token (accessToken / getAccessToken).
// Page the inbox (newest first). Views: all | unseen | unread | archived (default all).
const { notifications, cursor } = await sparkvault.notify.getInbox({ state: 'unread', limit: 50 });
// A page can come back SHORT of limit yet still carry a non-null cursor — always follow the cursor.
// Bounded unseen badge count; `capped` true means render "N+".
const { unread_count, capped } = await sparkvault.notify.getUnreadCount();
// Mark one of your rows seen | read | archived (idempotent). `createdAt` MUST be the row's own
// created_at, round-tripped from the list row — the id alone cannot address the row.
await sparkvault.notify.markState(row.notification_id, { createdAt: row.created_at, state: 'read' });
Browser Web Push
Opt the current browser into push notifications for the pointer (never content). Call from a user
gesture. Your app hosts and registers the service worker; the SDK handles the VAPID key, permission,
subscription, and server registration, returning a typed status instead of throwing on the expected
declines. subscribeWebPush returns not_configured until the platform VAPID
key is provisioned, so gate your UI on isWebPushSupported() and that status.
if (sparkvault.notify.isWebPushSupported()) {
const registration = await navigator.serviceWorker.register('/sw.js');
const { status } = await sparkvault.notify.subscribeWebPush({ serviceWorkerRegistration: registration });
// status: 'subscribed' | 'already_subscribed' | 'not_supported' | 'not_configured' | 'permission_denied'
// Later, to turn it off on this device:
await sparkvault.notify.unsubscribeWebPush({ serviceWorkerRegistration: registration });
}
Health API Reference
health.check(options?)
Check SparkVault API availability. Returns a structured result and does not throw for offline/network failures.
const result = await sparkvault.health.check({
timeout: 5000
});
// result.online - true when the API returns 2xx
// result.status - 'healthy' | 'unhealthy' | 'unreachable'
// result.httpStatus - HTTP status when a response was received
// result.checkedAt - Unix timestamp
// result.error - network/timeout message when unreachable
health.isOnline(options?)
Convenience method that resolves to a boolean.
const online = await sparkvault.health.isOnline({ timeout: 5000 });
Error Handling
try {
const result = await sparkvault.products.identity.signIn();
} catch (error) {
switch (error.code) {
case 'user_cancelled':
// User closed the popup (not an error)
break;
case 'validation_error':
console.error('Invalid input:', error.message);
break;
case 'network_error':
console.error('Network error');
break;
case 'timeout_error':
console.error('Request timed out');
break;
default:
console.error('Error:', error.message);
}
}
| Code | Description |
|---|---|
user_cancelled | User closed the popup |
validation_error | Invalid input or configuration (HTTP 400) |
authentication_error | Missing or invalid credentials (HTTP 401) |
authorization_error | Not permitted to access this resource (HTTP 403) |
PLAN_REQUIRED | The account has no active subscription and attempted a billable action (HTTP 402); surface a subscribe CTA to admins |
QUOTA_EXCEEDED | A pooled-capacity or seat limit is exhausted (HTTP 402); error.resource identifies the pool: storage | bandwidth | seats | identity |
network_error | Network connectivity failure |
timeout_error | Request timed out |
popup_blocked | Browser blocked popup |
The 401/403 and 402 billing gates apply to the authenticated vaults,
ingots, and sparks calls on this page. Each code maps to an exported
error class (e.g. PlanRequiredError, QuotaExceededError) for
instanceof checks.
Configuration Options
const sparkvault = SparkVault.init({
accountId: 'acc_YOUR_ACCOUNT_ID', // Required
backdropBlur: true, // Blur background on modals (default: true)
timeout: 30000, // HTTP timeout in ms (default: 30000)
preloadConfig: true // Preload config for instant modals (default: true)
});
Advanced options
| Parameter | Type | Required | Description |
|---|---|---|---|
apiBaseUrl |
string | Optional | Core API origin. A trailing /v1 is accepted and normalized.Default: https://api.sparkvault.com |
identityBaseUrl |
string | Optional | Identity Product API base URLDefault: https://auth.sparkvault.com |
portalBaseUrl |
string | Optional | auth.sv portal origin for portalUrl() deep linksDefault: https://auth.sv |
accessToken |
string | Optional | Static SparkVault Core access token for authenticated API calls |
getAccessToken |
function | Optional | Dynamic Core access-token provider (sync or async) for authenticated API calls |
apiKey |
string | Optional | API key for server-side SDK use |
clientId |
string | Optional | OIDC client ID registered for this account. Required only for FedCM one-click sign-in (signInWithFedcm()). |
allowedDownloadHostPatterns |
RegExp[] | Optional | Host allowlist for backend-issued ingot download URLs. Defaults to the canonical SparkVault Forge / S3 / CloudFront hosts; override only when pointing at a custom deployment. |
TypeScript
import SparkVault, {
SparkVaultConfig,
VerifyOptions,
VerifyResult,
UploadOptions,
UploadResult,
UserCancelledError,
ValidationError,
PlanRequiredError,
QuotaExceededError,
NetworkError
} from '@sparkvault/sdk-js';
const config: SparkVaultConfig = {
accountId: 'acc_YOUR_ACCOUNT_ID'
};
const sparkvault = SparkVault.init(config);
const result: VerifyResult = await sparkvault.products.identity.signIn();
Debug Mode
Enable verbose console logging:
<script
src="https://cdn.sparkvault.com/sdk/v1/sparkvault.js"
data-account-id="acc_YOUR_ACCOUNT_ID"
data-debug="true"
></script>
Browser Support
- Chrome 80+
- Firefox 75+
- Safari 13.1+
- Edge 80+
Passkey authentication requires WebAuthn support. On unsupported browsers, passkey won't appear as an option.