v2Recommended

Prism

One endpoint. Exactly the data you need.

Prism is the BetterSpace v2 API. Send one request, name the fields you want, and get back precisely that — across any number of modules, in a single round trip. No over-fetching, no waterfall of REST calls, no guessing which endpoint holds which field.

One round trip

Compose across modules in a single call.

25 operations

15 reads and 10 writes covering every module.

Same keys & billing

Nothing new to buy. Your v1 key just works.

# Two modules, one request curl -X POST https://database.betterspace.care/functions/v1/prism/v2 \ -H "Authorization: Bearer bs_live_your_key" \ -H "Content-Type: application/json" \ -d '{"query": "{ assessmentTypes { id name } therapists(minRating: 4.5) { id rating } }"}'

Why Prism

The same data, far fewer requests.

REST v1 — 3 requests

GET /v1/assessments/types GET /v1/therapists?min_rating=4.5 GET /v1/resources?type=meditation

Three round trips. Each returns every field whether you need it or not.

Prism v2 — 1 request

{ assessmentTypes { id name } therapists(minRating: 4.5) { id rating } resources(type: "meditation") { id title } }

One round trip, and only the fields you listed come back.

Getting started

If you already have a v1 API key, you are ready — there is nothing extra to enable.

Endpoint

POST https://database.betterspace.care/functions/v1/prism/v2

Authentication

Send your API key as a Bearer token. Same keys as REST v1.

Authorization: Bearer bs_live_your_api_key Content-Type: application/json
Never ship a secret key to a browser. For client-side apps, mint a short-lived session token from your backend and use that instead — see Browser & mobile clients.

Request body

FieldTypeNotes
querystringRequired. The operation to run.
variablesobjectOptional. Pass values instead of inlining them.
operationNamestringOptional. Required only if you send multiple operations.

Using variables

Prefer variables over string interpolation — safer and cacheable.

{ "query": "query Search($min: Float, $lang: String) { therapists(minRating: $min, language: $lang) { id rating sessionPrice } }", "variables": { "min": 4.5, "lang": "Hindi" } }

Tooling

Prism speaks a GraphQL-compatible wire format with introspection enabled, so any standard client, schema-codegen tool, or IDE plugin works against it without custom adapters.

Client examples

Any HTTP client works. Here are the common ones.

Node / TypeScript (no dependencies)

const res = await fetch("https://database.betterspace.care/functions/v1/prism/v2", { method: "POST", headers: { Authorization: `Bearer ${process.env.BETTERSPACE_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ query: `query Dashboard($uid: ID!) { moodTrends(userId: $uid, period: "weekly") { averageMood entries { value createdAt } } assessmentTypes { id name } }`, variables: { uid: "emp-001" }, }), }); const { data, errors } = await res.json(); if (errors) console.error(errors);

Python

import requests r = requests.post( "https://database.betterspace.care/functions/v1/prism/v2", headers={"Authorization": f"Bearer {KEY}"}, json={ "query": "mutation Log($uid: ID!, $v: Int!) { logMood(userId: $uid, moodValue: $v) { moodId createdAt } }", "variables": {"uid": "emp-001", "v": 4}, }, ) print(r.json()["data"])

Any GraphQL client (Apollo, urql, graphql-request)

import { GraphQLClient, gql } from "graphql-request"; const client = new GraphQLClient("https://database.betterspace.care/functions/v1/prism/v2", { headers: { Authorization: `Bearer ${process.env.BETTERSPACE_KEY}` }, }); const data = await client.request(gql` { therapists(minRating: 4.5, limit: 5) { id rating sessionPrice } } `);

Access & billing

Access is per capability — the same unit you already buy.

Every Prism field maps to the same capability as its REST counterpart. A field resolves if your plan includes that capability (every paid plan includes the full catalog). Therapy session fees are still a separate wallet debit. Access is not metered per module PEPM.

Partial results are normal. If a query asks for one entitled field and one unentitled field, Prism returns the entitled data in data and an entry in errors for the other. Always check both.

{ "data": { "assessmentTypes": [ { "id": "phq9" } ], "tests": null }, "errors": [ { "message": "Your account is not entitled to 'tests.list'.", "path": ["tests"], "extensions": { "code": "capability_not_entitled" } } ] }

Operations reference

All 25 operations. Arguments marked ! are required.

Reads

QUERYassessmentTypesassessments

Catalog of supported standardized screenings (PHQ-9, GAD-7, PSS-10, WHO-5, DASS-21).

assessmentTypes: [AssessmentType!]! → id, name, description, questions

Requires capability assessments.types

{ assessmentTypes { id name questions } }
QUERYtherapiststherapy_booking

Search the verified therapist directory. Returns name, photo, rates, session length, languages, and weekly schedule. Filters match stored values exactly (e.g. Anxiety, CBT).

therapists(specialization: String, language: String, minRating: Float, maxPrice: Float, page: Int, limit: Int): [Therapist!]!

Requires capability therapists.search

{ therapists(specialization: "Anxiety", maxPrice: 3000) { id name avatarUrl rating sessionPrice sessionDuration languages specializations timezone isAcceptingClients } }
QUERYtherapisttherapy_booking

Everything needed to render one therapist's profile page.

therapist(id: ID!): Therapist → full profile, or null if not found

Requires capability therapists.get

{ therapist(id: "uuid") { name bio avatarUrl experienceYears qualifications sessionPrice currency sessionDuration workingDays workingHoursStart workingHoursEnd timezone } }
QUERYtherapistAvailabilitytherapy_booking

Bookable slots for a date (YYYY-MM-DD). Booked times are already removed, so any returned slot can be booked. Check bookable/reason when the list is empty.

therapistAvailability(therapistId: ID!, date: String!): Availability!

Requires capability therapists.availability

{ therapistAvailability(therapistId: "uuid", date: "2026-08-20") { sessionDuration source bookable reason availableSlots { startTime endTime } } }
QUERYbookingtherapy_booking

Fetch a single booking, including its video room link once issued.

booking(id: ID!): Booking → status, paymentStatus, roomUrl, …

Requires capability bookings.get

QUERYbookingQuotetherapy_booking

Price a session before booking. Free — never charged, and works at zero wallet balance.

bookingQuote(therapistId: ID!): BookingQuote! → amount, therapistEarning, platformCommission

Requires capability bookings.quote

{ bookingQuote(therapistId: "uuid") { amount therapistEarning platformCommission sessionDuration } }
QUERYmoodTrendsjournal_mood

Per-user mood history and average over the chosen window.

moodTrends(userId: ID!, period: String): MoodTrends! period = "weekly" | "monthly" | "yearly"

Requires capability mood.trends

{ moodTrends(userId: "emp-001", period: "weekly") { averageMood entries { value createdAt } } }
QUERYteamAnalyticsteam_wellness

Aggregated, non-identifying wellness metrics for a team over the last 30 days.

teamAnalytics(teamId: ID!): TeamAnalytics! → memberCount, metrics { … }

Requires capability teams.analytics

QUERYteamBurnoutteam_wellness

Burnout risk score (0–100) derived from recent team mood signals.

teamBurnout(teamId: ID!): TeamBurnout! → burnoutRiskScore, riskLevel, recommendation

Requires capability teams.burnout

QUERYresourcescontent

Browse the content library — articles, meditations, and videos.

resources(category: String, type: String, page: Int, limit: Int): [Resource!]!

Requires capability resources.list

QUERYresourceCategoriescontent

Content categories, useful for building navigation.

resourceCategories: [ResourceCategory!]! → id, name, icon, color

Requires capability resources.categories

QUERYliveClassescontent

Upcoming live wellness classes.

liveClasses: [LiveClass!]! → title, scheduledAt, durationMinutes, …

Requires capability classes.list

QUERYtestspsychometric

Published psychometric tests available to your tenant.

tests: [Test!]! → id, title, category, questionCount, estimatedTimeMinutes

Requires capability tests.list

QUERYcrisisAlertscrisis_detection

Recent crisis alerts raised for your organization.

crisisAlerts: [CrisisAlert!]! → severity, riskCategory, status, createdAt

Requires capability crisis.alerts

QUERYuseralways-on

Look up a registered end user by your own external id. Free — no module required.

user(externalId: ID!): TenantUser → email, name, betterspaceUserId, isActive

Requires capability users.get

Writes

MUTATIONregisterUseralways-on

Create or link an end user. Call this first — every other user-scoped operation needs it. Free.

registerUser(input: { externalId: ID!, email: String!, name, phone, dateOfBirth, gender }): UserLink!

Requires capability users.register

mutation { registerUser(input: { externalId: "emp-001", email: "priya@co.com", name: "Priya Sharma" }) { status betterspaceUserId } }
MUTATIONadministerAssessmentassessments

Fetch the question set and instructions for an assessment.

administerAssessment(userId: ID!, assessmentType: String!): AssessmentForm!

Requires capability assessments.administer

mutation { administerAssessment(userId: "emp-001", assessmentType: "phq9") { instructions questions { id text options } } }
MUTATIONsubmitAssessmentassessments

Score and store answers. Returns score, severity band, and max score.

submitAssessment(userId: ID!, assessmentType: String!, answers: [Int!]!): AssessmentResult!

Requires capability assessments.submit

mutation { submitAssessment(userId: "emp-001", assessmentType: "phq9", answers: [1,2,1,3,0,1,2,1,0]) { score severity maxScore } }
MUTATIONcreateBookingtherapy_booking

Book a therapy session. The session fee is debited from your wallet and paid to the therapist — booking and payment are one atomic operation. Errors: insufficient_balance, slot_unavailable, user_not_linked, date_in_past.

createBooking(input: { userId: ID!, therapistId: ID!, date: String!, startTime: String!, endTime, notes }): BookingCreated!

Requires capability bookings.create

mutation { createBooking(input: { userId: "emp-001", therapistId: "uuid", date: "2026-08-20", startTime: "10:00:00" }) { bookingNumber status amountCharged therapistEarning walletBalancePaise } }
MUTATIONcancelBookingtherapy_booking

Cancel a booking. Refunds the wallet in full if the session has not started. Free, idempotent, and callable at zero balance.

cancelBooking(bookingId: ID!, reason: String): BookingCancelled!

Requires capability bookings.cancel

mutation { cancelBooking(bookingId: "uuid") { refunded refundAmount } }
MUTATIONlogMoodjournal_mood

Record a mood entry. moodValue must be 1–5.

logMood(userId: ID!, moodValue: Int!, activities: [String!], note: String): MoodLogged!

Requires capability mood.log

mutation { logMood(userId: "emp-001", moodValue: 4, activities: ["work"]) { moodId } }
MUTATIONlogSleepsleep_health

Record a sleep entry and get a computed sleep score.

logSleep(userId: ID!, bedtime: String!, wakeTime: String!, quality: Int, notes: String): SleepLogged!

Requires capability sleep.log

MUTATIONanalyzeCrisiscrisis_detection

Screen free text for suicide ideation, self-harm, or severe distress.

analyzeCrisis(text: String!, userId: ID): CrisisResult!

Requires capability crisis.analyze

mutation { analyzeCrisis(text: "I feel hopeless") { isCrisis severity recommendation } }
MUTATIONchatMessagerooh_ai

Send a message to Rooh, the white-label AI wellness companion. Pass sessionId to keep context.

chatMessage(userId: ID!, message: String!, sessionId: ID, context: String): ChatReply!

Requires capability chat.message

MUTATIONsendNotificationnotifications

Send a push, email, WhatsApp, or in-app notification. Respects user preferences and quiet hours.

sendNotification(userId: ID!, title: String!, body: String, channels: [String!]): NotificationResult!

Requires capability notifications.send

Browser & mobile clients

Two steps to call Prism safely from a browser or mobile app.

Secret keys must stay server-side. Exchange yours for a short-lived, user-scoped session token on your backend, then hand that token to the client.

// 1. On your server — mint a token (REST v1 endpoint) POST /v1/sessions/token { "user_id": "emp-001", "ttl_seconds": 900 } // → { "token": "bs_sess_…", "expires_in": 900 } // 2. In the browser — call Prism with it fetch("https://database.betterspace.care/functions/v1/prism/v2", { method: "POST", headers: { Authorization: `Bearer ${sessionToken}` }, body: JSON.stringify({ query: "{ assessmentTypes { id name } }" }) });

Session tokens are locked to the user they were minted for — Prism ignores any otheruserId the client sends — and cannot register users, mint tokens, or manage webhooks.

Error handling

Errors arrive in errors[], each with a stable extensions.code.

CodeMeaning
missing_api_keyNo Authorization header
invalid_api_keyKey invalid, expired, or account suspended
capability_not_entitledYour plan does not include this field's capability
capability_deniedCapability explicitly disabled for your account
session_forbiddenSession token attempted a secret-key-only operation
rate_limit_exceededToo many requests — check Retry-After
query_too_deepQuery nesting exceeded the depth limit of 12
query_parse_errorMalformed query syntax
invalid_requestMissing or invalid arguments
not_foundReferenced record does not exist

Limits

Max query depth is 12. Rate limits match your plan's per-minute allowance on v1 — read X-RateLimit-Remaining on every response.

Migrating from REST v1

Move at your own pace. REST v1 stays fully supported.

REST v1Prism v2
GET /v1/assessments/typesassessmentTypes
POST /v1/assessments/submitsubmitAssessment
GET /v1/therapiststherapists
GET /v1/therapists/:idtherapist
GET /v1/therapists/:id/availabilitytherapistAvailability
GET /v1/bookings/quotebookingQuote
POST /v1/bookingscreateBooking
POST /v1/bookings/:id/cancelcancelBooking
POST /v1/mood/loglogMood
GET /v1/mood/trendsmoodTrends
POST /v1/users/registerregisterUser
POST /v1/crisis/analyzeanalyzeCrisis
POST /v1/chat/messagechatMessage
POST /v1/notifications/sendsendNotification

No deadline, no dual billing

v1 and Prism run side by side on the same keys, capabilities, and meter. Migrate one call at a time, or mix both indefinitely. We will give long, explicit notice before any v1 change — and there is no plan to retire it.

Building a UI on top? The React UI kit ships prebuilt components that work with either version.

Need help? Contact us at api-support@betterspace.care