Skip to main content

Account Sessions

Account sessions provide secure, scoped tokens for client-side embedded components. Sessions are tied to a specific account and can be restricted to specific components.

Create a Session

TypeScript
const session = await dialstack.accountSessions.create({
account: 'acct_01h2xcejqtf2nbrexx3vqjhp41',
components: {
call_logs: { enabled: true },
voicemails: { enabled: true },
},
});

console.log(session.client_secret); // 'cs_live_...'
console.log(session.expires_at); // '2025-01-15T13:00:00Z'

Parameters

ParameterTypeRequiredDescription
accountstringYesAccount ID to scope the session to
componentsobjectYesComponent permissions. At least one component must be enabled — a missing, empty, or all-disabled set is rejected.

Response

TypeScript
interface AccountSessionCreateResponse {
client_secret: string;
expires_at: string;
}

Component Permissions

Control which embedded components the session can access:

TypeScript
const session = await dialstack.accountSessions.create({
account: 'acct_01h2xcejqtf2nbrexx3vqjhp41',
components: {
call_logs: { enabled: true },
voicemails: { enabled: true },
},
});

Available Components

ComponentDescription
call_logsAccess to call history
voicemailsAccess to voicemails
call_historyAccess to the call-history component
phone_numbersManage the account's phone numbers
phone_number_orderingSearch for and order new phone numbers
dial_planView and edit call routing
ring_groupsManage ring groups
qualityCall-quality summary and worst calls
ai_agentConfigure the AI agent
account_onboardingThe onboarding portal, including reading the subscription agreement
agreement_acceptanceAccepting the subscription agreement — see the warning below

:::warning agreement_acceptance is an attestation

This component lets the session bind the account to the DialStack Service Subscription Agreement — a legal commitment by your customer. It is separate from account_onboarding on purpose: a session with onboarding alone can read the agreement and render the accept screen, so someone who may not sign still learns that service is blocked, but the submission is rejected.

DialStack does not decide who may sign, because the person is your user, not ours. You do, by enabling this component. By minting a session with it, you assert that you presented the agreement to someone entitled to accept on that account's behalf. Enable it for that person and leave it off for everyone else. DialStack's own admin portal, for reference, grants it only to the account's owner.

If you render the onboarding portal, pass canAcceptAgreement with the same condition so a non-signer sees an explanation instead of a button that fails.

:::

Restricting Access

TypeScript
// Only allow voicemails
const session = await dialstack.accountSessions.create({
account: accountId,
components: {
call_logs: { enabled: false },
voicemails: { enabled: true },
},
});

If a disabled component is used, it will show an error state.

Session Lifecycle

Expiration

Sessions expire after 1 hour by default. The expires_at field indicates when the session becomes invalid.

Client-Side Handling

Pass the full session response to enable automatic refresh:

TypeScript
// Server
app.post('/api/dialstack/session', async (req, res) => {
const session = await dialstack.accountSessions.create({
account: req.body.accountId,
components: {
call_logs: { enabled: true },
voicemails: { enabled: true },
},
});

// Return both fields
res.json({
client_secret: session.client_secret,
expires_at: session.expires_at,
});
});
TypeScript
// Client (React)
<DialstackComponentsProvider
dialstack={dialstack}
clientSecret={{
client_secret: session.client_secret,
expires_at: session.expires_at,
}}
>

Manual Refresh

If the session expires and you only passed the client_secret string:

TypeScript
// Client-side
async function refreshSession() {
const response = await fetch('/api/dialstack/session', {
method: 'POST',
body: JSON.stringify({ accountId }),
});
const session = await response.json();
setClientSecret(session.client_secret);
}

Security

API Key Required

Sessions can only be created with an API key (sk_live_*). This prevents clients from creating their own sessions.

TypeScript
// This works (server-side with API key)
const dialstack = new DialStack(process.env.DIALSTACK_API_KEY);
const session = await dialstack.accountSessions.create({
account: accountId,
components: { call_logs: { enabled: true } },
});

// Client-side tokens cannot create sessions
// (no accountSessions.create method available)

Account Isolation

Each session is scoped to a single account. The client_secret can only access data belonging to that account.

Token Security

  • Tokens cannot be used to create new sessions
  • Tokens are short-lived (1 hour)
  • Tokens have limited scope (only enabled components)

Examples

Express.js Session Endpoint

TypeScript
import express from 'express';
import { DialStack } from '@dialstack/sdk-server';

const app = express();
const dialstack = new DialStack(process.env.DIALSTACK_API_KEY);

app.use(express.json());

app.post('/api/dialstack/session', async (req, res) => {
try {
const { accountId } = req.body;

// Verify user has access to this account
const userId = req.user?.id; // From auth middleware
const hasAccess = await verifyAccountAccess(userId, accountId);

if (!hasAccess) {
return res.status(403).json({ error: 'Access denied' });
}

// Create session
const session = await dialstack.accountSessions.create({
account: accountId,
components: {
call_logs: { enabled: true },
voicemails: { enabled: true },
},
});

res.json(session);
} catch (error) {
console.error('Session creation failed:', error);
res.status(500).json({ error: 'Failed to create session' });
}
});

Next.js API Route

TypeScript
// pages/api/dialstack/session.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { DialStack } from '@dialstack/sdk-server';
import { getServerSession } from 'next-auth';

const dialstack = new DialStack(process.env.DIALSTACK_API_KEY!);

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}

// Check authentication
const authSession = await getServerSession(req, res);
if (!authSession) {
return res.status(401).json({ error: 'Unauthorized' });
}

const { accountId } = req.body;

// Verify user owns this account
const userAccount = await db.accounts.findFirst({
where: {
dialstackAccountId: accountId,
userId: authSession.user.id,
},
});

if (!userAccount) {
return res.status(403).json({ error: 'Access denied' });
}

// Create session
const session = await dialstack.accountSessions.create({
account: accountId,
components: {
call_logs: { enabled: true },
voicemails: { enabled: true },
},
});

res.json(session);
}

Component-Specific Sessions

TypeScript
// Only voicemails for this user
async function getVoicemailSession(accountId: string) {
return dialstack.accountSessions.create({
account: accountId,
components: {
call_logs: { enabled: false },
voicemails: { enabled: true },
},
});
}

// Only call logs for analytics dashboard
async function getAnalyticsSession(accountId: string) {
return dialstack.accountSessions.create({
account: accountId,
components: {
call_logs: { enabled: true },
voicemails: { enabled: false },
},
});
}

Deprecated: sessions

The sessions property is deprecated. Use accountSessions instead:

TypeScript
const params = {
account: accountId,
components: { call_logs: { enabled: true } },
};

// Deprecated
const session = await dialstack.sessions.create(params);

// Use this instead
const session = await dialstack.accountSessions.create(params);

Next Steps