curl --request POST \
--url https://your-app.com/assessment/session \
--header 'Authorization: Bearer <recruiter-jwt>' \
--header 'Content-Type: application/json' \
--data '{
"users": [
{ "name": "Priya Sharma", "email": "priya@example.com" },
{ "name": "Rahul Verma", "email": "rahul@example.com" },
{ "name": "Aisha Khan", "email": "aisha@example.com" }
]
}'
// This is exactly how your ProfileList.tsx calls this endpoint
const response = await assessmentApi.createSession(
data.map(c => ({
name: c.name ?? c.fullName ?? '',
email: c.email ?? '',
}))
);
const token = response?.data?.token;
const AssessmentClient = require('@recordorg/smartai-assessment-backend');
app.post('/assessment/session', yourAuthMiddleware, async (req, res) => {
const client = new AssessmentClient({
apiKey: process.env.ASSESSMENT_API_KEY,
secretKey: process.env.ASSESSMENT_SECRET_KEY,
});
const { users } = req.body;
if (!users?.length) {
return res.status(400).json({ success: false, message: 'No candidates provided' });
}
const session = await client.createSession({
users,
recruiterId: req.user?.id, // pass recruiterId
recruiterEmail: req.user?.email, // or recruiterEmail — at least one required
});
res.json({ success: true, data: session });
});
from fastapi import FastAPI, HTTPException
from smartai_assessment_backend import AssessmentClient
from pydantic import BaseModel
import os
app = FastAPI()
client = AssessmentClient(
api_key=os.getenv("ASSESSMENT_API_KEY"),
secret_key=os.getenv("ASSESSMENT_SECRET_KEY"),
)
class User(BaseModel):
name: str
email: str
class SessionRequest(BaseModel):
users: list[User]
recruiter_id: str | None = None
recruiter_email: str | None = None
@app.post("/assessment/session")
def create_assessment_session(body: SessionRequest):
if not body.users:
raise HTTPException(status_code=400, detail="No candidates provided")
if not body.recruiter_id and not body.recruiter_email:
raise HTTPException(
status_code=400,
detail="Provide at least one of recruiter_id or recruiter_email",
)
users = [{"name": u.name, "email": u.email} for u in body.users]
session = client.create_session(
users=users,
recruiter_id=body.recruiter_id,
recruiter_email=body.recruiter_email,
)
return {"success": True, "data": session}
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiJzZXNzXzAxIn0.HMAC"
}
}
{
"success": false,
"message": "No candidates provided"
}
{
"success": false,
"message": "SmartAI assessment credentials are not configured"
}
REST API
Create Session
Your backend endpoint that creates a session token for the SmartAI portal. Your frontend calls this before opening the assessment.
POST
/
assessment
/
session
curl --request POST \
--url https://your-app.com/assessment/session \
--header 'Authorization: Bearer <recruiter-jwt>' \
--header 'Content-Type: application/json' \
--data '{
"users": [
{ "name": "Priya Sharma", "email": "priya@example.com" },
{ "name": "Rahul Verma", "email": "rahul@example.com" },
{ "name": "Aisha Khan", "email": "aisha@example.com" }
]
}'
// This is exactly how your ProfileList.tsx calls this endpoint
const response = await assessmentApi.createSession(
data.map(c => ({
name: c.name ?? c.fullName ?? '',
email: c.email ?? '',
}))
);
const token = response?.data?.token;
const AssessmentClient = require('@recordorg/smartai-assessment-backend');
app.post('/assessment/session', yourAuthMiddleware, async (req, res) => {
const client = new AssessmentClient({
apiKey: process.env.ASSESSMENT_API_KEY,
secretKey: process.env.ASSESSMENT_SECRET_KEY,
});
const { users } = req.body;
if (!users?.length) {
return res.status(400).json({ success: false, message: 'No candidates provided' });
}
const session = await client.createSession({
users,
recruiterId: req.user?.id, // pass recruiterId
recruiterEmail: req.user?.email, // or recruiterEmail — at least one required
});
res.json({ success: true, data: session });
});
from fastapi import FastAPI, HTTPException
from smartai_assessment_backend import AssessmentClient
from pydantic import BaseModel
import os
app = FastAPI()
client = AssessmentClient(
api_key=os.getenv("ASSESSMENT_API_KEY"),
secret_key=os.getenv("ASSESSMENT_SECRET_KEY"),
)
class User(BaseModel):
name: str
email: str
class SessionRequest(BaseModel):
users: list[User]
recruiter_id: str | None = None
recruiter_email: str | None = None
@app.post("/assessment/session")
def create_assessment_session(body: SessionRequest):
if not body.users:
raise HTTPException(status_code=400, detail="No candidates provided")
if not body.recruiter_id and not body.recruiter_email:
raise HTTPException(
status_code=400,
detail="Provide at least one of recruiter_id or recruiter_email",
)
users = [{"name": u.name, "email": u.email} for u in body.users]
session = client.create_session(
users=users,
recruiter_id=body.recruiter_id,
recruiter_email=body.recruiter_email,
)
return {"success": True, "data": session}
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiJzZXNzXzAxIn0.HMAC"
}
}
{
"success": false,
"message": "No candidates provided"
}
{
"success": false,
"message": "SmartAI assessment credentials are not configured"
}
You build this endpoint. The path
/assessment/session is whatever route you choose in your own app. Internally it calls client.createSession() from the Backend SDK and returns the token to your frontend.How it fits in the flow
Your Frontend (ProfileList.tsx)
│
│ POST /assessment/session
│ { users: [{ name, email }, ...] }
│
▼
Your Backend (this endpoint)
│
│ client.createSession({ users, recruiterId?, recruiterEmail? })
│
▼
SmartAI Platform
│
│ { token, expiresAt }
│
▼
Your Backend returns { success: true, data: { token, expiresAt } }
│
▼
Your Frontend calls AssessmentPortal.open({ token })
Environment variables required
Add these to your server’s.env file before this endpoint will work:
ASSESSMENT_API_KEY=VFN_TEST_your_key_here
ASSESSMENT_SECRET_KEY=VFN_SK_TEST_your_secret_here
Never put these in your frontend
.env. They belong on the server only. Your frontend uses NEXT_PUBLIC_ASSESSMENT_API_KEY (the public API key) separately just to tell the portal which environment to use — the secret key never leaves your server.Request
Headers
string
required
Your app’s authentication token for the logged-in recruiter. Format:
Bearer <jwt>.
Protect this endpoint so only your own recruiter accounts can call it.string
required
Must be
application/json.Body
array
required
Array of candidate objects to include in this assessment session. Must have at least one item.
Hide Each user object
Hide Each user object
[
{ "name": "Priya Sharma", "email": "priya@example.com" },
{ "name": "Rahul Verma", "email": "rahul@example.com" },
{ "name": "Aisha Khan", "email": "aisha@example.com" }
]
string
Your internal recruiter ID. Pass this or
recruiterEmail — at least one is required.string
The recruiter’s email address. Pass this or
recruiterId — at least one is required.Response
boolean
true when the session was created successfully.object
Show Session data
Show Session data
string
A session token. Pass this directly to
AssessmentPortal.open({ token }) in your frontend.Do not store or reuse this token. Request a fresh one every time a recruiter starts a new session.curl --request POST \
--url https://your-app.com/assessment/session \
--header 'Authorization: Bearer <recruiter-jwt>' \
--header 'Content-Type: application/json' \
--data '{
"users": [
{ "name": "Priya Sharma", "email": "priya@example.com" },
{ "name": "Rahul Verma", "email": "rahul@example.com" },
{ "name": "Aisha Khan", "email": "aisha@example.com" }
]
}'
// This is exactly how your ProfileList.tsx calls this endpoint
const response = await assessmentApi.createSession(
data.map(c => ({
name: c.name ?? c.fullName ?? '',
email: c.email ?? '',
}))
);
const token = response?.data?.token;
const AssessmentClient = require('@recordorg/smartai-assessment-backend');
app.post('/assessment/session', yourAuthMiddleware, async (req, res) => {
const client = new AssessmentClient({
apiKey: process.env.ASSESSMENT_API_KEY,
secretKey: process.env.ASSESSMENT_SECRET_KEY,
});
const { users } = req.body;
if (!users?.length) {
return res.status(400).json({ success: false, message: 'No candidates provided' });
}
const session = await client.createSession({
users,
recruiterId: req.user?.id, // pass recruiterId
recruiterEmail: req.user?.email, // or recruiterEmail — at least one required
});
res.json({ success: true, data: session });
});
from fastapi import FastAPI, HTTPException
from smartai_assessment_backend import AssessmentClient
from pydantic import BaseModel
import os
app = FastAPI()
client = AssessmentClient(
api_key=os.getenv("ASSESSMENT_API_KEY"),
secret_key=os.getenv("ASSESSMENT_SECRET_KEY"),
)
class User(BaseModel):
name: str
email: str
class SessionRequest(BaseModel):
users: list[User]
recruiter_id: str | None = None
recruiter_email: str | None = None
@app.post("/assessment/session")
def create_assessment_session(body: SessionRequest):
if not body.users:
raise HTTPException(status_code=400, detail="No candidates provided")
if not body.recruiter_id and not body.recruiter_email:
raise HTTPException(
status_code=400,
detail="Provide at least one of recruiter_id or recruiter_email",
)
users = [{"name": u.name, "email": u.email} for u in body.users]
session = client.create_session(
users=users,
recruiter_id=body.recruiter_id,
recruiter_email=body.recruiter_email,
)
return {"success": True, "data": session}
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiJzZXNzXzAxIn0.HMAC"
}
}
{
"success": false,
"message": "No candidates provided"
}
{
"success": false,
"message": "SmartAI assessment credentials are not configured"
}
Common mistakes
| Mistake | What happens | Fix |
|---|---|---|
ASSESSMENT_API_KEY not set in .env | Returns 500 | Add the key to your server .env |
Sending users: [] empty array | Returns 400 | Make sure candidates are selected before calling |
Neither recruiterId nor recruiterEmail sent | Returns 400 | Pass at least one recruiter field |
| Calling this from the browser directly | API key exposed in network tab | Always call through your backend |
| Reusing the same token for a second session | Token expired error | Always request a fresh token |
⌘I

