Authentication
DialStack supports three authentication methods for different use cases.
| Method | Format | Scope | Use case |
|---|---|---|---|
| API keys | sk_live_* prefix | Platform-wide | Server-to-server integrations (provisioning, analytics) |
| Session tokens | JWT | Single account | Embedded UI components (call logs, voicemails) |
| User tokens | JWT | Single user | Softphones and user-facing apps (WebRTC, call history) |
API Keys
Your secret API key is used for all server-side API requests.
curl https://api.dialstack.ai/v1/accounts \
-H "Authorization: Bearer sk_live_YOUR_SECRET_KEY"
Never expose your secret key in client-side code, public repositories, or browser applications. Store it securely in environment variables on your server.
Account Context
Most endpoints require an account context. Include the DialStack-Account header:
curl https://api.dialstack.ai/v1/users \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41"
Getting Your API Keys
API keys are provided during platform onboarding. Contact support@dialstack.ai if you need access.
Session Tokens
For embedded voice components in your frontend, create a session using the Account Session API. Sessions are account-scoped and expire after 1 hour.
import { DialStack } from '@dialstack/sdk/server';
const dialstack = new DialStack(process.env.DIALSTACK_API_KEY);
const session = await dialstack.accountSessions.create({
account: 'acct_01h2xcejqtf2nbrexx3vqjhp41',
components: {
call_logs: { enabled: true },
voicemails: { enabled: true },
},
});
// Pass session.client_secret to your frontend
The account is automatically derived from the JWT claims — no DialStack-Account header needed.
User Tokens
User authentication (including the /v1/auth/token endpoint and user-token-based access) is currently undergoing implementation and will be available shortly. The surface documented below reflects the target design; specifics may change before release.
For client-side applications where end users interact directly (softphones, call history, voicemail). User tokens are scoped to a single user within an account.
Use API keys when your backend is making requests on behalf of your platform. Use session tokens for embedded UI components. Use user tokens when the end user's device connects to DialStack directly (WebRTC softphone, mobile app).
How User Tokens Work
┌──────────────┐ ┌──────────────────┐ ┌───────────────┐
│ Your App │ │ Your Backend │ │ DialStack │
│ (browser / │ │ │ │ │
│ mobile) │ │ │ │ │
└──────┬───────┘ └────────┬─────────┘ └───────┬───────┘
│ 1. User logs in │ │
│─────────────────────>│ │
│ │ 2. POST /v1/auth/token
│ │──────────────────────>│
│ │ │
│ │ 3. { user_token } │
│ │<──────────────────────│
│ 4. Return token │ │
│<─────────────────────│ │
│ │ │
│ 5. Connect to /v1/webrtc with token │
│─────────────────────────────────────────────>│
- The user logs into your application using your own authentication
- Your backend calls
POST /v1/auth/tokenwith the user's identity - DialStack returns a user token (JWT)
- Your backend passes the token to the client
- The client uses the token to connect to the WebRTC signalling channel and access user-scoped REST endpoints
Platform Setup
Configure token exchange for your platform during onboarding. This lets your backend exchange your own JWTs for DialStack user tokens — your users authenticate once with your app and get seamless access to DialStack calling with no additional login prompt.
Configuration requires:
- JWKS URL — where DialStack fetches your public keys to verify your JWTs
- Issuer — the
issclaim value in your JWTs - Audience — the
audclaim value DialStack expects (typicallydialstack) - User ID claim — which JWT claim maps to the DialStack user (typically
sub)
Contact your DialStack account team to configure these settings.
Mapping Users
Set the external_id field when creating users to match the user's identifier in your system:
const user = await dialstack.users.create(
{
name: 'Jane Doe',
email: 'jane@example.com',
external_id: 'your-system-user-id-123',
},
{ dialstackAccount: 'acct_01h2xcejqtf2nbrexx3vqjhp41' }
);
Obtaining a User Token
- SDK
- cURL
const { token, expires_at, user } = await dialstack.auth.createToken({
grant_type: 'token_exchange',
subject_token: platformJwt, // Your platform's JWT for this user
subject_token_type: 'urn:ietf:params:oauth:token-type:jwt',
account_id: 'acct_01h2xcejqtf2nbrexx3vqjhp41',
});
curl -X POST https://api.dialstack.ai/v1/auth/token \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"grant_type": "token_exchange",
"subject_token": "eyJhbGciOiJSUzI1NiIs...",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"account_id": "acct_01h2xcejqtf2nbrexx3vqjhp41"
}'
Response:
{
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_at": "2026-04-10T22:00:00Z",
"expires_in": 3600,
"user": {
"id": "user_01h2xcejqtf2nbrexx3vqjhp42",
"name": "Jane Doe",
"email": "jane@example.com",
"account_id": "acct_01h2xcejqtf2nbrexx3vqjhp41"
}
}
Token Lifecycle
User tokens expire after 1 hour. Refresh proactively (e.g., 5 minutes before expiry) to avoid interrupting active WebRTC sessions.
const { token, expires_at } = await dialstack.auth.refreshToken({
token: currentUserToken,
});
Revoke a token when the user logs out:
curl -X POST https://api.dialstack.ai/v1/auth/revoke \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "token": "eyJhbGciOiJFZERTQSIs..." }'
Revoking a token disconnects any active WebRTC session using that token.
Scoped Access
User tokens grant access to a limited set of endpoints:
| Endpoint | Method | Description |
|---|---|---|
/v1/webrtc | WebSocket | Signalling channel for WebRTC calls |
/v1/webrtc/ice-servers | GET | TURN/STUN server credentials |
/v1/me | GET | Authenticated user's profile |
/v1/me/calls | GET | User's own call history |
/v1/voicemails | GET | User's voicemails |
/v1/voicemails/{id} | GET, POST, DELETE | Single voicemail |
/v1/voicemails/{id}/transcript | GET | Voicemail transcript |
/v1/me/presence | GET, PUT | User's presence status |
/v1/me/emergency-address | GET, PUT | E911 emergency address |
User tokens cannot access platform-level endpoints (accounts, phone numbers, dial plans, etc.). Those require API keys. Session tokens and user tokens are both JWTs but not interchangeable — using the wrong token type returns 403 Forbidden.
Error Responses
401 Unauthorized
{
"error": "Invalid API key",
"code": "authentication_failed"
}
Common causes:
- Missing
Authorizationheader - Invalid or expired API key / token
- Wrong key format
403 Forbidden
{
"error": "You don't have permission to access this resource",
"code": "forbidden"
}
Common causes:
- Trying to access another platform's resources
- Using a user token on a platform-level endpoint
- Using a session token on a user-scoped endpoint
Best Practices
- Store API keys in environment variables, never in client-side code
- Use secrets management tools (AWS Secrets Manager, HashiCorp Vault)
- Refresh user tokens proactively before expiry
- Revoke user tokens on logout
Next Steps
- Quickstart Guide — Build your first integration
- WebRTC Guide — Build a softphone with user tokens
- API Reference — Full API documentation