Adds a Transcribe button to upload collections that accept audio, sending files to the VocalStack service for speech-to-text. The transcript and timed sentences with speaker labels are saved in a vocalstack field group on the document. It adds a settings global and endpoints, and connects with an API key or one-click OAuth. Requires a VocalStack account and Payload 3.
VocalStack for Payload CMS: a Transcribe button on every audio upload.
Installation
npm i @vocalstack/payload0
Stars
0
Forks
0
Open Issues
Today
Last Updated
0
Weekly Downloads
v0.1.0
npm Version
143.6 KB
Unpacked Size
0
Dependencies
Trends appear after 7 days of data. Collecting since Oct 22 — 1 daily snapshot so far.
Trends appear after 7 days of data. Collecting since Oct 22 — 1 daily snapshot so far.
Each point is npm's rolling 7-day download count.
Health Score
Composite score based on GitHub activity, stars, npm downloads, publish recency, dependency count, and package size.
Badge for your README
[](https://payloaddirectory.dev/plugins/1423030201-root)<a href="https://payloaddirectory.dev/plugins/1423030201-root"><img src="https://payloaddirectory.dev/badge/1423030201-root.svg" alt="Payload Directory"></a>README
VocalStack for Payload CMS 3: a Transcribe button on every audio upload. The transcript is saved on the document as text plus timed sentences.
pnpm add @vocalstack/payload
// payload.config.ts
import { vocalstackPlugin } from '@vocalstack/payload'
export default buildConfig({
plugins: [
vocalstackPlugin({
// Optional. Without it, connect from Admin → Settings → VocalStack.
apiKey: process.env.VOCALSTACK_API_KEY,
}),
],
})
Then run payload generate:importmap. The plugin adds fields to the targeted collections and a vocalstack global, so with Postgres also run payload migrate:create.
audio/*, a wildcard, or no restriction), or the ones in collections, gets:
audio/* documents only. It shows progress and survives a reload mid-job, and Stop ends a job you no longer want: the document goes back to how it was (an earlier transcript stays), and VocalStack keeps what it transcribed before the stop.vocalstack group:
transcript: text you can edit; paragraphs break on a speaker change or a pause.sentences: [{ start, end, speaker, text }] in seconds.status, language, error, recordingId, transcriptionId and transcribedAt.POST /api/{collection}/:id/vocalstack/transcribe, POST …/vocalstack/stop, GET …/vocalstack/status and GET …/vocalstack/stream (live progress as Server-Sent Events).| Option | Default | |
|---|---|---|
apiKey | none | A vs_key_… key with Read and Write. It wins over a key connected in the admin. |
collections | every upload collection that accepts audio | Which upload collections get the button. |
audioFields | none | Audio that isn't an upload document; see below. |
spokenLanguage | auto-detect | A Whisper language code (en, ro, …). |
fileURL | the doc's url, made absolute | ({ doc, collection, req }) => string: the address VocalStack fetches. Point it at your bucket's public URL to skip the Payload file proxy. |
access | signed-in users who can update the doc | Extra check for transcribing and for changing the connection. |
oneClickConnect | false | Show Connect VocalStack (see below). |
apiURL / dashboardURL | https://api.vocalstack.com / https://app.vocalstack.com | |
enabled | true | false keeps the schema but removes the button and the endpoints. |
Imported content often points at files by name or link instead of upload documents. audioFields gives each row of an array field its own button and vocalstack transcript:
vocalstackPlugin({
audioFields: [
{
collection: 'episodes',
field: 'attachments', // a top-level array (tabs, rows and collapsibles are looked through)
condition: (row) => row.type === 'file',
audioURL: ({ row }) => (row.filename ? `https://cdn.example.com/episodes/${encodeURIComponent(String(row.filename))}` : undefined),
},
],
})
Rows are addressed by their id: POST /api/episodes/:id/vocalstack/transcribe?field=attachments&row=<row id>. A new row needs saving first.
How the audio reaches VocalStack. If the file has a public address, VocalStack fetches it (POST /v1/transcriptions { url }). Otherwise (localhost, a private network) the plugin uploads the bytes. Either way the job runs on VocalStack, and the admin polls every 3 s. The account is billed per second, with a 5-minute minimum per transcription.
The alternative to pasting a key: Connect VocalStack opens the dashboard in a new tab, the person clicks Authenticate, and the tab closes. This is OAuth 2.0 authorization code + PKCE (RFC 7636) with an API key as the access token. There is no client registration: the consent screen shows the redirect_uri host instead.
CMS admin CMS server app.vocalstack.com / api.vocalstack.com
────────── ────────── ───────────────────────────────────────
click Connect ── POST /api/vocalstack/connect ─▶ state, verifier (10 min)
◀──────────── { url } ──────────────
new tab ─▶ GET app/connect?response_type=code&client_name=…&redirect_uri=…&state=…
&code_challenge=…&code_challenge_method=S256&scope=read write
(sign in if needed; pick the organization; "example.com wants to transcribe with VocalStack")
click Authenticate ─▶ API stores a single-use grant (code hash, challenge, redirect, scopes, principal; 2 min)
◀── 302 redirect_uri?code=…&state=…
tab ─────── GET /api/vocalstack/callback ─▶ check state (+ admin session)
POST api/v1/connect/token (form) ─▶ grant_type=authorization_code, code,
code_verifier, redirect_uri
verify, burn the code, create the key
◀── { access_token: "vs_key_…", token_type: "api_key", key_id, account: { name } }
stores the key (never sent to a browser)
tab: "Connected", BroadcastChannel('vocalstack') ─▶ admin refreshes; tab closes
Cancel redirects with ?error=access_denied&state=…. The key appears in the dashboard's API keys list as example.com (Payload CMS), where it can be revoked.
payload ai
AI text, image and voice generation inside Payload rich text and upload fields, with bring-your-own-model support
811.7k/wk558v3
payload simple social login
Google and Microsoft social login for the Payload admin panel and auth collections
650/wk3v3
payload auth
Replace Payload's built-in auth with Better Auth for a shared admin and frontend session
590/wk344v3
payload ai manager
Generate product titles, descriptions, attributes and translations with Claude, ChatGPT, Gemini or Kimi
585/wk1v3
payload oauth2
Generic OAuth2 sign-in for Payload CMS with support for providers such as Google, Zitadel and Apple
570/wk198v3
payload authjs
Use Auth.js 5 sign-in providers to authenticate users in Payload CMS 3
560/wk207v3