Authentication
OpenCap Stack supports three authentication methods. Choose the one that fits your use case.
| Method | Best For | Token Lifetime |
|---|---|---|
| AINative SSO | Users who already have an AINative account | 7 days (refresh) |
| Email / Password | Standard login, your own user base | 7 days (refresh) |
| Agent Onboarding | AI agents, CI/CD, server-to-server | Non-expiring API key |
All methods return a JWT Bearer token used in the Authorization header:
Authorization: Bearer <your-token>
AINative SSO
If your users have AINative accounts, you can authenticate them against OpenCap Stack without a separate credential.
Server-Side Flow (Recommended)
Send the user's AINative credentials to OpenCap Stack's proxy endpoint — no redirect required:
curl -X POST https://api.opencapstack.com/api/v1/auth/ainative-login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "ainative-password"
}'
Response:
{
"token": "eyJhbGci...",
"refreshToken": "eyJhbGci...",
"user": {
"id": "usr_abc123",
"email": "user@example.com",
"name": "Jane Doe",
"companyId": "co_xyz789"
}
}
This endpoint:
- Validates the credentials against the AINative
/api/v1/auth/loginAPI - Fetches the AINative user profile via
/api/v1/auth/me - Provisions (or retrieves) a matching OpenCap Stack user
- Returns an OpenCap Stack JWT
Using the Token
const response = await fetch('https://api.opencapstack.com/api/v1/auth/ainative-login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
const { token } = await response.json();
// Store and use for subsequent requests
const data = await fetch('https://api.opencapstack.com/api/v1/stakeholders', {
headers: { Authorization: `Bearer ${token}` },
});
Next.js / React Integration
// lib/authService.js
export async function loginWithAINative(email, password) {
const res = await fetch('/api/v1/auth/ainative-login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
if (!res.ok) throw new Error('Authentication failed');
const { token, user } = await res.json();
// Set cookie for Edge Middleware auth
document.cookie = `session=${token}; path=/; secure; samesite=strict`;
return { token, user };
}
The full OAuth 2.1 PKCE browser redirect flow (/oauth/authorize) is available for registered redirect URIs. Contact AINative to add your domain to the allowlist. For most integrations the server-side proxy above is simpler and works immediately.
Email / Password
Standard registration and login for users with their own OpenCap Stack accounts.
Register
curl -X POST https://api.opencapstack.com/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Doe",
"email": "jane@startup.com",
"password": "securepassword123"
}'
Response:
{
"token": "eyJhbGci...",
"refreshToken": "eyJhbGci...",
"user": {
"id": "usr_abc123",
"email": "jane@startup.com",
"name": "Jane Doe"
}
}
Login
curl -X POST https://api.opencapstack.com/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "jane@startup.com",
"password": "securepassword123"
}'
Refresh Token
JWTs expire after 7 days. Use the refresh token to get a new one without re-authenticating:
curl -X POST https://api.opencapstack.com/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{ "refreshToken": "eyJhbGci..." }'
Agent Onboarding
The fastest path for AI agents, automation scripts, and server-to-server integrations. Zero human steps required.
curl -X POST https://api.opencapstack.com/api/v1/agents/onboard \
-H "Content-Type: application/json" \
-d '{
"agent_id": "my-cap-table-agent",
"agent_name": "Cap Table Automation",
"capabilities": [
"read:cap-table",
"write:stakeholders",
"write:equity",
"read:financials"
]
}'
Response:
{
"token": "eyJhbGci...",
"api_key": "ocs_live_abc123xyz...",
"company_id": "co_xyz789",
"agent_id": "my-cap-table-agent",
"capabilities": ["read:cap-table", "write:stakeholders", "write:equity", "read:financials"],
"provisioned_at": "2026-05-13T00:00:00Z"
}
Store both the token (JWT) and api_key — the API key doesn't expire and can be used directly.
Available Capability Scopes
| Scope | Access |
|---|---|
read:cap-table | Stakeholders, share classes, equity grants, ownership percentages |
write:stakeholders | Create, update, delete stakeholders |
write:equity | Issue grants, update vesting schedules, create share classes |
read:financials | 409A valuations, financial reports, fundraising data |
write:documents | Upload and manage legal documents |
read:analytics | Reporting, dilution models, waterfall analysis |
write:safe-notes | Create and update SAFE instruments |
admin | Full access — use only for trusted internal agents |
Using the API Key
# API key works as a Bearer token
curl https://api.opencapstack.com/api/v1/stakeholders \
-H "Authorization: Bearer ocs_live_abc123xyz..."
import requests
API_KEY = "ocs_live_abc123xyz..."
BASE_URL = "https://api.opencapstack.com/api/v1"
headers = {"Authorization": f"Bearer {API_KEY}"}
stakeholders = requests.get(f"{BASE_URL}/stakeholders", headers=headers).json()
Bring Your Own Auth
If you're self-hosting OpenCap Stack, you can configure any JWT-compatible identity provider.
Set these environment variables:
JWT_SECRET=your-256-bit-secret
JWT_ISSUER=https://your-auth-provider.com # optional
JWT_AUDIENCE=opencapstack # optional
Your tokens must be signed RS256 or HS256 JWTs with a sub (user ID) claim. The middleware validates the signature and extracts the user from your existing user table.
For OIDC providers (Auth0, Clerk, Supabase Auth):
OIDC_ISSUER=https://your-tenant.auth0.com/
OIDC_AUDIENCE=https://api.opencapstack.com
See the Self-Hosting guide for full configuration.
Token Security
- Store tokens in
httpOnlycookies or server-side sessions — neverlocalStoragefor production - The Next.js Edge Middleware validates the
sessioncookie on every protected route - Refresh tokens are single-use and rotated on each refresh
- Agent API keys can be revoked via
DELETE /api/v1/agents/{agent_id}