Contents
Dictum SDK
Voice input infrastructure for product interfaces.
Dictum adds WAV speech capture, explicit transcription states, secure model routing, and final transcript insertion to desktop and mobile web products. The SDK is browser-only and headless: your product always owns the UI.
Browser only
Desktop web and mobile web
7 states
One explicit state machine
18 events
Distinct stable names, at most 16 characters
0 provider keys
No speech vendor secrets in the browser
Quickstart
Ship voice input in one integration pass.
Start with a public project key, a server-issued identity token, and one stable source label for the app surface where users will speak.
Install
npm
# Registry publication is prepared but not active yet.
# Once released:
npm install dictum-sdk- 1. Create a Dictum project and copy the public key.
- 2. Mint a signed identity from your backend under the global, non-configurable seven-day maximum.
- 3. Check browser compatibility, then listen only for provider-confirmed final transcript segments.
Headless SDK
Use this when the voice control must feel native.
The SDK owns recording, state transitions, edge routing, and transcript events. Your app owns placement, styling, editor insertion, and recovery UI.
Client integration
TypeScript
import { checkCompatibility, createSession } from "dictum-sdk";
const compatibility = checkCompatibility();
if (!compatibility.supported) {
showUnsupportedBrowser(compatibility.missing);
} else {
const session = createSession({
projectKey: "dpk_live_xxx",
identityToken: await getDictumIdentityToken(),
sourceLabel: "editor-main",
target: document.querySelector("#message")
});
session.on("state_changed", event => renderVoiceState(event.state));
session.on("transcript_delta", event => renderFinalSegment(event.data.text));
session.on("sdk_error", event => showVoiceError(event.data));
startButton.onclick = () => session.start().catch(showVoiceError);
pauseButton.onclick = () => session.pause().catch(showVoiceError);
resumeButton.onclick = () => session.resume().catch(showVoiceError);
stopButton.onclick = () => session.stop().catch(showVoiceError);
retryButton.onclick = () => session.retry().catch(showVoiceError);
const result = await session.result;
renderFinalTranscript(result.text, result.transport);
}checkCompatibility()Reports required browser capabilities without changing state or requesting microphone access.
createSession()Creates a one-shot session, locks its target, and preloads remote project configuration.
start()Requests microphone access and resolves only when the session is listening.
pause()Pauses capture while keeping the session and UI mounted.
resume()Continues capture on the same session.
stop()Ends capture and completes transcription on the session transport.
retry()Explicitly retries a retryable network failure over POST with retained WAV audio.
destroy()Idempotently releases microphone, transport, listeners, retained audio, and the pending result.
resultSettles once with the final transcript result, or rejects on a terminal failure or destruction.
Browser support
Detect capabilities before showing voice UI.
Compatibility is capability-based, never user-agent based. Unsupported browsers expose the exact missing APIs so the host can disable voice input without requesting the microphone. Non-browser consumers use the Dictum API directly.
Capability preflight
Browser
const report = checkCompatibility();
if (!report.supported) {
voiceButton.disabled = true;
showMessage("Please update your browser to use voice input.");
console.debug({ capture: report.capture, missing: report.missing });
}Identity
Sign users server-side, then capture in the browser.
Signed identities are scoped to a project and stable customer subject. Their global, non-configurable maximum lifetime is seven days. The browser receives the identity; the signing key stays on your server.
Backend token route
Server
import { createHmac } from "node:crypto";
const MAX_IDENTITY_LIFETIME_SECONDS = 7 * 24 * 60 * 60;
const encodeJson = value => Buffer.from(JSON.stringify(value)).toString("base64url");
function createDictumIdentity({ keyId, signingSecret, projectKey, userId }) {
const iat = Math.floor(Date.now() / 1000);
const header = { alg: "HS256", typ: "JWT", kid: keyId };
const payload = {
sub: String(userId),
aud: projectKey,
iat,
exp: iat + MAX_IDENTITY_LIFETIME_SECONDS
};
const unsignedToken = [encodeJson(header), encodeJson(payload)].join(".");
const signature = createHmac("sha256", signingSecret)
.update(unsignedToken)
.digest("base64url");
return [unsignedToken, signature].join(".");
}
app.post("/api/dictum/identity", requireUser, (request, response) => {
const identityToken = createDictumIdentity({
keyId: process.env.DICTUM_SIGNING_KEY_ID,
signingSecret: process.env.DICTUM_SIGNING_SECRET,
projectKey: process.env.DICTUM_PROJECT_KEY,
userId: request.user.id
});
response.json({ identityToken });
});Project public key
Allowed in the browser. Identifies the project and routing profile.
Signed identity
Generated server-side, with one global non-configurable maximum lifetime of seven days, and binds the customer user to the project.
Signing key
Server-only. Never place this in frontend code, HTML, logs, or analytics payloads.
Provider API keys
Stored in the Dictum dashboard vault or your backend. Never expose them to users.
Events
Render UI from states. Persist from final output.
Every `transcript_delta` segment is provider-confirmed, final, immutable, and append-only. The SDK never exposes interim hypotheses. Usage, duration, and transcription counts are measured by the Worker rather than submitted by the browser.
Insertion helper
DOM
session.on("transcript_delta", event => {
// Every segment is provider-confirmed, final, immutable and append-only.
renderFinalSegment(event.data.text);
});
session.on("text_inserted", event => {
showInsertedState(event.data.insertMode);
});
session.result.then(result => {
renderFinalTranscript(result.text);
});Event payloads
state_changed{ previousState, nextState }A valid state-machine transition completed.
volume_changed{ level, rms?, peak? }Opt-in local volume feedback while listening.
transcript_delta{ text, transport }Provider-confirmed final append-only transcript segment.
text_inserted{ text, insertMode }The SDK inserted final text into the locked target.
mic_deniedpublic errorThe browser denied microphone permission.
no_transcript{ reason, error }No transcript was produced because audio was empty or contained no speech.
network_errorpublic errorA retryable network, timeout, or stream loss occurred.
audio_invalidpublic errorCaptured audio violated the WAV contract.
compat_failed{ code, action, details }Required browser capabilities are unavailable.
limit_reached{ reason, durationMs, maxSeconds, bytes, maxBytes, action }The Worker reached its streaming audio boundary and forced transcription.
fallback_started{ from, to, reason }WebSocket recovery actually started over POST.
quota_exceededpublic errorThe account or user exhausted its usage quota.
auth_requiredpublic errorA signed customer identity is missing.
identity_expiredpublic errorThe signed identity expired; the host must reload it.
auth_invalidpublic errorThe signed identity or project authorization was rejected.
retry_started{ transport: "post" }An explicit user retry started.
sdk_error{ code, message, retryable, userActionRequired, action? }General safe public error event emitted for every session failure.
State machine
idleThe one-shot session is ready; no microphone capture is active.
requesting_micThe browser is resolving permission and capture startup.
listeningWAV capture is active.
pausedCapture is paused while the same session and target remain locked.
retryingAn explicit POST retry is running from retained WAV audio.
transcribingCapture ended and the final transcript is completing.
failedA typed public failure occurred; retry is offered only when the error says it is retryable.
Errors
Every voice UI needs recovery states.
Treat Dictum errors as product states, not console messages. Keep user text intact, expose retry where safe, and show account or permission actions when retrying cannot fix the issue.
microphone_permission_deniedUser or browser denied microphone permission.
Explain browser permission settings, then create a new session after access is granted.
microphone_not_foundNo microphone is available to the browser.
Ask the user to connect or select a microphone, then create a new session.
browser_unsupportedThe selected capture path needs browser capabilities that are unavailable.
Disable voice input and ask the user to update their browser.
sdk_disabledDictum is disabled for this project.
Keep voice input disabled and ask the project owner to enable the SDK.
network_errorThe browser could not reach the Dictum edge service.
Offer retry() only while retryable is true.
stream_lostThe active transcription connection was interrupted.
Keep the retained audio and offer the explicit POST retry.
transcription_failedThe provider could not complete the transcription.
Preserve the host input and let the user start a new session.
transport_response_invalidThe transcription transport returned an invalid success response.
Do not retry automatically; report the protocol failure to the service owner.
empty_audioThe completed session contains no usable audio.
Keep the target intact and let the user start a new session.
no_speech_detectedThe Worker completed without a transcript.
Keep the target intact and let the user start a new session.
quota_exceededThe project has reached its current usage limit.
Disable capture and link to billing or usage controls.
unsupported_audio_typeThe capture adapter supplied audio outside the WAV contract.
Fix the adapter to emit audio/wav and create a new session.
audio_too_largeThe transcription audio payload exceeded the accepted request size.
Keep the target intact and start a shorter dictation.
rate_limitedThe service is temporarily rate limited.
Offer retry() only while retryable is true.
timeoutThe transcription request stopped making network progress.
Offer retry() only while retryable is true.
aborted_by_userThe user or host stopped the session.
Treat it as a deliberate cancellation and leave the target intact.
client_not_authenticatedThe signed customer identity is missing.
Sign the user in, fetch a signed identity, and create a new session.
identity_expiredThe signed customer identity has expired.
Reload the page with a freshly signed identity.
config_response_invalidThe project configuration response is incomplete or invalid.
Keep voice input disabled and report the server configuration failure.
session_response_invalidThe session service returned an invalid success response.
Do not retry automatically; report the protocol failure to the service owner.
auth_config_errorThe signed identity or its project/signing-key configuration was rejected.
Issue a new identity with the correct kid and aud, then create a new session.
Security
Keep speech vendors and signing material behind your backend.
Browser integrations should identify the project and current user, not carry secret credentials. Provider keys and signing keys belong in controlled server or dashboard storage.
Service surfaces
Dashboard API
https://dictum-dashboard-api.agentcore.workers.devProject, sites, models, provider keys, signing keys, account, and usage.
SDK API
https://dictum-sdk-api.agentcore.workers.devBrowser-facing facade. Its config check confirms that the project is recognized and active so the client page can enable its Dictum integration; WebSocket is always attempted and is not a configurable feature.
Billing API
https://billing-app.agentcore.workers.dev/dictumCheckout, billing status, and customer portal.
Dashboard account API
/api/dashboard/accountPublic dashboard proxy for account usage and quota state; the usage Worker remains private behind a service binding.
AI Readers
Implementation contract for agents.
If an AI agent reads this page to implement Dictum, it should use the browser-only headless SDK for web UI, send non-browser integrations to the API, never expose server-only secrets, and consume only provider-confirmed final transcript segments.
Machine-readable summary
JSON
{
"product": "Dictum",
"capability": "headless voice-to-text for desktop and mobile web browsers",
"browserOnly": true,
"nonBrowserIntegration": "Use the Dictum API directly",
"package": "dictum-sdk",
"packagePublished": false,
"browserSecrets": [
"project public key",
"signed customer identity under the global non-configurable seven-day maximum"
],
"serverOnlySecrets": [
"Dictum signing key",
"speech provider API keys"
],
"states": [
"idle", "requesting_mic", "listening", "paused", "retrying", "transcribing", "failed"
],
"events": [
"state_changed", "volume_changed", "transcript_delta", "text_inserted",
"mic_denied", "no_transcript", "network_error", "audio_invalid",
"compat_failed", "limit_reached", "fallback_started", "quota_exceeded",
"auth_required", "identity_expired", "auth_invalid", "retry_started", "sdk_error"
],
"transport": "WebSocket first; explicit bounded recovery to POST",
"transcriptRule": "Every transcript_delta segment is provider-confirmed, final, immutable, and append-only.",
"usageRule": "Usage is measured by the Worker, never submitted by the browser SDK.",
"privacyRule": "Never log raw audio, transcript text, signed identity, or provider secrets."
}Checklist
Production readiness before launch.
Create a project and copy the public project key.
Create a server-side signing key for identity tokens.
Add one source label per website, app surface, or workflow.
Choose provider/model routing and save provider keys in the vault.
Use the authorized SDK bundle until the prepared npm release is activated.
Run checkCompatibility() before enabling the voice control.
Handle state_changed, transcript_delta, sdk_error, and relevant dedicated events.
Verify insertion behavior in every target input, textarea, and editor.
Offer retry() only for retryable network failures and destroy the session on unmount.
Monitor Worker-owned usage and error codes from the dashboard.