Overview
VedaStudy SSB is a living library of sacred texts: every text in several language editions (IAST/English, Devanāgarī, Tamil, Telugu, Kannada, Russian…), each always at its latest version. When a text is corrected and re-uploaded, everything built on this platform shows the new version automatically — links, embeds and API answers never point at an old file.
What you can build with:
- Embedded reader — drop the full VedaStudy SSB reader into any page: PDF with page navigation, language switcher, Chant & Read (tap a line to hear it, follow along, slow down, repeat) and line meanings (tap a line for its in-depth meaning). One line of HTML.
- Sign in with VedaStudy SSB — your visitors sign in (or sign up) with their VedaStudy SSB account: an emailed code, on VedaStudy SSB's own page. You get their name, and can read/save texts in their library.
- REST API — folders, documents, every edition's current file, page text, chant timings, meanings, search, "what changed" per version.
- MCP server — connect Claude, ChatGPT, Cursor or any MCP client; the assistant can search the library, read texts and generate the integration code for you.
- Webhooks — get told the moment a new version is published.
Getting access
A VedaStudy SSB admin creates an app for you (Admin → Developers) and gives you:
| Credential | Looks like | Where to use it |
|---|---|---|
| Publishable key | pk_live_… | In web pages: embeds, the JS SDK, browser API calls. Only works from your app's allowed origins (e.g. https://your-site.org). It is also your OAuth client_id. |
| Secret key | sk_live_… | Only on your server (API calls, OAuth token exchange). Shown once; can be rotated. Never put it in a web page or a URL. |
| Webhook secret | whsec_… | To verify webhook signatures. |
Each app also has a content access setting chosen by VedaStudy SSB:
- Signed-in (default) — titles, folders and thumbnails are open to your key, but reading/listening needs the visitor to sign in with VedaStudy SSB (the embed handles this by itself).
- Open — anyone on your site can read and listen without an account.
Private (staff-only) items are never available through the platform.
Base URLs
- API:
https://vedastudyssb.web.app/api/v1(OpenAPI:https://vedastudyssb.web.app/api/v1/openapi.json) - MCP:
https://vedastudyssb.web.app/api/mcp - OAuth metadata:
https://vedastudyssb.web.app/.well-known/oauth-authorization-server - JS SDK:
https://vedastudyssb.web.app/sdk/v1.js
Embed the reader
The quickest way — web component
<script src="https://vedastudyssb.web.app/sdk/v1.js" data-key="pk_live_YOUR_PUBLISHABLE_KEY" defer></script>
<vedastudy-reader doc="DOCUMENT_ID_OR_SLUG" lang="en" chant meanings height="85vh"></vedastudy-reader>Attributes:
| Attribute | Meaning |
|---|---|
doc | Document id or slug (required). Find them with the API or the MCP search tool. |
lang | Edition to open: en (IAST), hi, ta, te, kn, ru… The reader still lets people switch. |
page | Open on this page. |
line | Open on (and highlight) this line key, e.g. 12:0. |
chant | Start with Chant & Read on (when the text has recordings). |
meanings | Start with line meanings shown. |
ui | Interface language of the reader's buttons (e.g. hi, ta, es); defaults to the visitor's browser language. |
hide-header | Hide the title bar (when your page already shows the title). |
height | CSS height of the frame (default 80vh). |
The element fires DOM events you can listen to: vedastudy:ready, vedastudy:page (detail.page), vedastudy:line (detail.key, when a line is played or opened), vedastudy:signin (detail.signedIn).
document.querySelector("vedastudy-reader").addEventListener("vedastudy:line", (e) => console.log(e.detail));Plain iframe
<iframe src="https://vedastudyssb.web.app/embed/DOCUMENT_ID?key=pk_live_YOUR_PUBLISHABLE_KEY&lang=en&chant=1"
style="width:100%;height:85vh;border:0;border-radius:12px"
allow="autoplay; fullscreen; clipboard-write" loading="lazy"></iframe>The embed only loads on your app's allowed origins (it sends Content-Security-Policy: frame-ancestors with them). For local development ask for http://localhost:3000 (any port you use) to be added.
Signed-in apps
If your app is "signed-in", the reader shows a Sign in with VedaStudy SSB button inside the frame. It opens VedaStudy SSB's sign-in in a popup (new members can sign up there), and the reader remembers the visitor on your site afterwards. You don't need any code for this.
Always the latest version
The embed resolves the edition's current version every time it loads. When an admin uploads a corrected PDF, every embed shows it — including the chant timings and meanings, which are re-placed on the new pages automatically.
Sign in with VedaStudy SSB
Members sign in on VedaStudy SSB's own page — with an emailed 6-digit code — and new visitors can create their VedaStudy SSB account right there. Your app never sees a password or code; it receives an access token.
In the browser (JS SDK)
<script src="https://vedastudyssb.web.app/sdk/v1.js" data-key="pk_live_YOUR_PUBLISHABLE_KEY" defer></script>
<vedastudy-signin></vedastudy-signin> <!-- a ready-made button -->
<script>
window.addEventListener("vedastudy:auth", (e) => {
const user = e.detail.user; // { id, name, email, country, preferredLanguage } or null
console.log(user ? "Hello " + user.name : "signed out");
});
async function custom() {
const { user } = await VedaStudy.signIn({ scope: "profile library" }); // opens a popup
const lib = await VedaStudy.api("/me/library"); // calls the API as the member
await VedaStudy.signOut();
}
</script>The SDK uses OAuth 2.1 with PKCE in a popup, keeps tokens in localStorage for your site, refreshes them automatically, and exposes VedaStudy.getUser(), VedaStudy.getAccessToken(), VedaStudy.api(path, init), VedaStudy.signIn(), VedaStudy.signOut().
Server-side (any language) — standard OAuth 2.1
- Register your callback URL with the VedaStudy SSB admin (e.g.
https://your-site.org/auth/vedastudy/callback). - Send the visitor to:
https://vedastudyssb.web.app/oauth/authorize?response_type=code
&client_id=pk_live_YOUR_PUBLISHABLE_KEY
&redirect_uri=https://your-site.org/auth/vedastudy/callback
&scope=profile%20library
&state=RANDOM_STATE
&code_challenge=BASE64URL_SHA256_OF_VERIFIER&code_challenge_method=S256- VedaStudy SSB redirects back with
?code=…&state=…. Exchange it from your server:
curl -X POST https://vedastudyssb.web.app/api/oauth/token \
-u "pk_live_YOUR_PUBLISHABLE_KEY:sk_live_YOUR_SECRET" \
-d grant_type=authorization_code -d code=CODE \
-d redirect_uri=https://your-site.org/auth/vedastudy/callback \
-d code_verifier=VERIFIERResponse: { "access_token": "vsat.…", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "vsrt_…", "scope": "profile library" }. Refresh with grant_type=refresh_token (refresh tokens rotate — store the new one). PKCE is optional when you authenticate with the secret key, required otherwise.
- Call
GET https://vedastudyssb.web.app/api/oauth/userinfo(or/api/v1/me) withAuthorization: Bearer <access_token>.
| Scope | Grants |
|---|---|
| (always) | Read texts as the member (content of "signed-in" apps) |
profile | Name, email, country, preferred language |
library | Read and change the member's saved texts |
meanings:write | Write line meanings — only granted to VedaStudy SSB staff |
classes | Classes, homework, tutor feedback (tutors: run their classes) |
classes:submit | Send a recording to the tutor, only when the member asks |
practice | Practice progress |
notes | Their own highlights, bookmarks and notes |
Extra parameters: prompt=consent (always ask), prompt=login (switch account), prompt=none (silent; returns error=login_required / consent_required). Members see and disconnect apps under Settings → Connected apps; sign them out with POST https://vedastudyssb.web.app/api/oauth/revoke (token=…).
Discovery: https://vedastudyssb.web.app/.well-known/oauth-authorization-server.
REST API
Base URL https://vedastudyssb.web.app/api/v1. Send Authorization: Bearer <pk_live_… | sk_live_… | vsat.…>. JSON in, JSON out. Full schema: https://vedastudyssb.web.app/api/v1/openapi.json.
curl -H "Authorization: Bearer sk_live_YOUR_SECRET" "https://vedastudyssb.web.app/api/v1/documents?limit=5"// In the browser with the publishable key (from an allowed origin)
const r = await fetch("https://vedastudyssb.web.app/api/v1/search?q=kavacam", { headers: { Authorization: "Bearer pk_live_YOUR_PUBLISHABLE_KEY" } });| Endpoint | What |
|---|---|
GET /languages | Content languages |
GET /folders?parent= | Folders (top level or under a folder) |
GET /folders/{id} | A folder: sub-folders, documents, breadcrumb |
GET /documents | List/filter: folder, q, kind, lang, updated_since, sort=updated, limit, cursor |
GET /documents/{id} | A document (id or slug) with every edition: current version number, date, changelog, size, pages, features, links |
GET /documents/{id}/thumbnail | Preview image (works as <img src="…?key=pk_live_…">) |
GET /documents/{id}/file?lang= | Signed link to the current file (&redirect=1 to 302, &download=1 to save) · content |
GET /documents/{id}/video?lang= | Video player embed URL · content |
GET /documents/{id}/text?lang=&from=&to= | Page-by-page text · content |
GET /documents/{id}/chant?lang= | Chant & Read timings · content |
GET /documents/{id}/meanings?lang= | Line meanings · content |
GET /documents/{id}/changes?lang= | Pages changed in the current version |
GET /documents/{id}/embed?lang=&chant=1&meanings=1 | Ready-made embed HTML |
GET /search?q=&text=1 | Titles, folders and text inside documents |
GET /me | The signed-in member (profile) |
GET /me/library, PUT/DELETE /me/library/{id} | Their saved texts (library) |
PUT/DELETE /documents/{id}/meanings/{key} | Write a meaning (staff, meanings:write) |
"content" = needs an "open" app or a member's access token. Titles are returned in English unless you pass ?locale=hi (etc.); every object also carries titles in all languages.
Links you receive (links.api, links.embed, links.web) always resolve to the current version — store ids/slugs, not file URLs. File URLs from /file are signed and expire (15 min; 6 h for audio).
Chant & Read and line meanings
Chant & Read
Some texts have recordings synchronised line by line. GET /documents/{id}/chant?lang=ta returns:
{
"sections": [{ "index": 0, "id": "…", "title": "Devī Kavacam", "audioUrl": "https://…m4a (signed, 6h)" }],
"lines": [{ "index": 0, "key": "0:0", "section": 0, "start": 0.0, "end": 7.94,
"boxes": [{ "page": 3, "x": 0.12, "y": 0.18, "w": 0.7, "h": 0.02 }], "text": "…" }]
}Play a line: load audioUrl, set currentTime = start, pause at end. boxes are fractions of the page (0–1, from the top-left) on this edition's current version, so you can draw highlights over your own rendering. The same key identifies the line in every edition. The embedded reader does all of this for you.
Line meanings
VedaStudy SSB staff write an in-depth meaning (word-by-word, translation, commentary) for individual lines. A meaning is written once and appears on every language edition and every future version of the text — in the VedaStudy SSB app, in every embed, and through the API:
GET /documents/{id}/meanings?lang=hi
{ "sourceLanguage": "en",
"meanings": [{ "key": "12:0", "verse": 12, "text": "…the line in IAST…",
"body": { "en": "**Word by word** …markdown…", "hi": "…" },
"boxes": [{ "page": 7, "x": 0.1, "y": 0.42, "w": 0.75, "h": 0.02 }] }] }body is Markdown keyed by language. Staff (and AI assistants acting for staff with the meanings:write scope) can write them: PUT /documents/{id}/meanings/{key} with {"body": {"en": "…"}, "status": "published"} — or in the reader itself (tap a line with Meanings on).
Classes, recordings & practice
Members can join a tutor's chanting class, get homework (lines to practise), send a recording of a line to the tutor, and read the tutor's feedback. Tutors run classes, set homework, listen and give feedback. All of it works for a signed-in member through the API and the MCP server, with the same rules as the app — a student only ever sees their own recordings, a tutor only their own classes.
| Scope | Lets the app… |
|---|---|
classes | see the member's classes, homework, feedback; join/leave; and for tutors: start classes, set homework, hear recordings, give feedback |
classes:submit | send a recording to the tutor — only when the member asks |
practice | read their practice progress (takes, best scores, learn-by-heart reviews due) |
notes | read and change their own highlights, bookmarks and notes |
| Endpoint | What |
|---|---|
GET /me/classes | Classes (taught ones include the join code), homework, recordings sent + feedback |
POST /me/classes/join | {"code": "AB3CD9"} |
DELETE /me/classes/{id} | Leave a class |
POST /classes | Start a class {"name": …} (tutors) |
GET /classes/{id} | Tutor: students, homework, recordings, join code · Student: homework + own recordings |
POST /classes/{id}/homework | {"document", "chapter", "verse_from", "verse_to"} (verse numbers restart each chapter) or {"document", "keys": ["12:0", …]}, optional due, note (tutors) |
DELETE /homework/{id} | Remove homework (tutors) |
POST /classes/{id}/recordings | Send a recording: multipart audio file + meta JSON {document, line_key, message?, homework_id?}, or JSON with audio_base64 + mime_type (≤ 6 MB, audio/*) |
GET /submissions/{id}/recording | Signed link to hear it (sender or tutor; ?redirect=1 to 302) |
POST /submissions/{id}/feedback | {"text": …} — the student is emailed (tutors) |
GET /me/practice?doc= | Practice per line (or reviews due across texts) |
GET /me/notes?doc= · PUT /me/notes/{doc}/{line} | Notes: {highlight, bookmark, note} |
curl -X POST https://vedastudyssb.web.app/api/v1/classes/CLASS_ID/recordings \
-H "Authorization: Bearer vsat.…" \
-F audio=@take.webm -F 'meta={"document":"dm-full-sb-master","line_key":"11:0","message":"Verse 12, please check my svaras"}'Recordings sent through the API or an AI assistant arrive "not rated" with a "sent from an app" badge — scores are only given by the VedaStudy SSB app, which rates the chanting on the member's own device. Line keys ("12:0") come from find_lines / the chant and meanings endpoints and are the same in every edition.
MCP server (AI assistants)
https://vedastudyssb.web.app/api/mcp is a Model Context Protocol server (Streamable HTTP). Give it to your AI assistant and it can search the library, read texts and chant timings, look up meanings, and write the embed / sign-in code for the site you're building.
Claude (claude.ai / Desktop) → Settings → Connectors → Add custom connector — URL https://vedastudyssb.web.app/api/mcp. Claude signs you in with VedaStudy SSB (no key needed).
Claude Code
claude mcp add --transport http vedastudy https://vedastudyssb.web.app/api/mcp
# or with an app key:
claude mcp add --transport http vedastudy https://vedastudyssb.web.app/api/mcp --header "Authorization: Bearer sk_live_YOUR_SECRET"Cursor / VS Code / others (mcp.json):
{ "mcpServers": { "vedastudy": { "url": "https://vedastudyssb.web.app/api/mcp",
"headers": { "Authorization": "Bearer sk_live_YOUR_SECRET" } } } }Authentication: an app key (sk_live_…, or pk_live_…) in the Authorization header, or OAuth — clients that support MCP authorization discover https://vedastudyssb.web.app/.well-known/oauth-protected-resource, register themselves, and send you through Sign in with VedaStudy SSB.
Tools — library: search, browse, list_documents, get_document, get_document_text, find_lines, get_chant_timings, get_meanings, write_meaning (staff), get_embed_code, get_file_link, get_recent_updates, get_version_changes, list_languages. Member: my_library, my_classes, class_details, join_class, leave_class, send_recording (only when asked), get_recording, my_practice, my_notes. Tutors: create_class, set_homework, remove_homework, give_feedback. Also whoami, developer_guide. Prompt: build_site.
Webhooks & staying up to date
Give the admin an https URL and VedaStudy SSB POSTs JSON to it:
{ "id": "evt_…", "type": "document.version.published", "createdAt": "2026-…",
"data": { "documentId": "…", "slug": "…", "language": "ta", "version": 7, "changelog": "…", "updatedPages": "3–5, 12" } }Events: document.version.published, meaning.published.
Verify the VedaStudy-Signature: t=<unix>,v1=<hex> header: v1 = HMAC_SHA256(whsec_…, "<t>.<raw body>").
import crypto from "node:crypto";
function verify(raw, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const mac = crypto.createHmac("sha256", secret).update(t + "." + raw).digest("hex");
return crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(v1)) && Math.abs(Date.now() / 1000 - t) < 300;
}No webhook? Poll GET /api/v1/documents?updated_since=<last sync ISO date>&sort=updated.
Limits, errors & guidelines
- Rate limits (per server instance, per minute): secret key 1200, publishable key 600 per visitor IP, member token 600. Over the limit →
429withRetry-After. - Errors are
{ "error": { "code": "…", "message": "…" } }— codes:unauthorized,forbidden,not_found,bad_request,sign_in_required,origin_not_allowed,secret_key_in_browser,insufficient_scope,rate_limited,app_suspended,server_error. - Keys: never ship
sk_live_to a browser or commit it; ask an admin to rotate if it leaks. Publishable keys only work from your allowed origins. - Attribution: keep the "VedaStudy SSB" credit the embed shows, and link texts back to VedaStudy SSB when you show them outside the embed.
- Don't re-host files — always link/stream through the API or embed, so readers get corrections the moment they're published.
- Respect the sacred nature of the material: no ads injected into the reader, no edits to the texts.
