Skip to main content

Kryptos Connect

Kryptos Connect enables users to securely share their crypto portfolio data with third-party applications through a single, easy-to-integrate widget.

Overview

Kryptos Connect provides a seamless way for users to:

  • Authorize access to their crypto portfolio with explicit consent
  • Share data securely using industry-standard OAuth 2.0
  • Manage integrations by connecting wallets and exchanges
  • Control permissions with granular scope-based access
  • Long-lived access with 15-year access tokens (no refresh tokens needed)
  • Native mobile support for iOS and Android applications

Base URL

https://connect-api.kryptos.io

Getting Started

Prerequisites

  1. Register your application on the Developer Portal to receive:

    • client_id - Your application identifier
    • client_secret - Your secret key (keep secure, never expose in frontend)
  2. Configure your client with:

    • Allowed redirect URIs
    • Allowed origins for CORS
    • Required scopes

Integration Steps

1. CREATE LINK TOKEN (Backend)
Your server calls /link-token with client credentials

2. INITIALIZE WIDGET (Frontend)
Pass link_token to the SDK widget

3. USER AUTHENTICATES
User logs in or creates anonymous account

4. USER GRANTS CONSENT
User approves requested permissions

5. RECEIVE PUBLIC TOKEN
Widget returns public_token via onSuccess callback

6. EXCHANGE TOKEN (Backend)
Your server exchanges public_token for access_token

7. ACCESS DATA
Use access_token to call Data APIs

Authentication Methods

MethodHeader/BodyUsed For
Client CredentialsX-Client-Id + X-Client-Secret headersCreating link tokens
Link TokenX-LINK-TOKEN headerWidget operations
Bearer TokenAuthorization: Bearer {access_token}Data API calls

Web SDK

The Kryptos Connect Web SDK provides React components that handle the complete authentication and wallet connection flow. It supports 5000+ DeFi protocols, 100+ exchanges/wallets, and 200+ blockchains.

Installation

npm install @kryptos_connect/web-sdk

Prerequisites

  1. Client ID from the Kryptos Developer Portal
  2. WalletConnect Project ID from WalletConnect Cloud

Quick Start

// 1. Import styles (required)
import "@kryptos_connect/web-sdk/dist/styles/index.css";

import {
KryptosConnectProvider,
KryptosConnectButton,
} from "@kryptos_connect/web-sdk";

// 2. Wrap your app with the provider
function App() {
return (
<KryptosConnectProvider
config={{
appName: "My DeFi App",
appLogo: "https://yourapp.com/logo.png",
clientId: "your-kryptos-client-id",
theme: "light",
walletConnectProjectId: "your-walletconnect-project-id",
}}
>
<ConnectButton />
</KryptosConnectProvider>
);
}

// 3. Use the connect button
function ConnectButton() {
return (
<KryptosConnectButton
generateLinkToken={async () => {
// Call your backend to create a link token
const response = await fetch("/api/kryptos/create-link-token", {
method: "POST",
headers: { "Content-Type": "application/json" },
});
const data = await response.json();
return {
link_token: data.link_token,
isAuthorized: data.isAuthorized,
};
}}
onSuccess={(userConsent) => {
if (userConsent?.public_token) {
// New user - exchange public token for access token
fetch("/api/kryptos/exchange-token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ public_token: userConsent.public_token }),
});
}
// If userConsent is null, user was already authenticated
}}
onError={(error) => {
console.error("Connection failed:", error);
}}
>
Connect to Kryptos
</KryptosConnectButton>
);
}

Provider Configuration

Wrap your application with KryptosConnectProvider:

OptionTypeRequiredDescription
appNamestringYesYour application name
appLogostring | React.ReactNodeNoLogo URL or React component
clientIdstringYesKryptos client ID from Developer Portal
theme"light" | "dark"NoVisual theme (default: "light")
walletConnectProjectIdstringYesWalletConnect project ID

Button Configuration

The KryptosConnectButton component triggers the connection flow:

OptionTypeRequiredDescription
generateLinkToken() => Promise<{ link_token: string; isAuthorized?: boolean }>YesFunction that returns link token from your backend
onSuccess(userConsent: UserConsent | null) => voidNoCallback on successful connection
onError(error: Error) => voidNoCallback on connection failure
childrenReact.ReactNodeNoButton text (default: "Connect to Kryptos")
classNamestringNoCustom CSS class
styleReact.CSSPropertiesNoInline styles

TypeScript Support

import type {
KryptosConnectButtonProps,
KryptosConnectProviderProps,
UserConsent,
} from "@kryptos_connect/web-sdk";

interface UserConsent {
public_token: string;
}

Customization

CSS Variables

:root {
--kc-primary: #8b5cf6;
--kc-primary-hover: #7c3aed;
--kc-bg-primary: #ffffff;
--kc-text-primary: #000000;
--kc-border: #e5e7eb;
--kc-border-radius: 8px;
}

[data-kc-theme="dark"] {
--kc-bg-primary: #1a1a1a;
--kc-text-primary: #ffffff;
--kc-border: #404040;
}

Custom Button Styling

<KryptosConnectButton
className="my-custom-button"
style={{
background: "linear-gradient(to right, #667eea, #764ba2)",
borderRadius: "12px",
}}
>
Connect Wallet
</KryptosConnectButton>

Framework Integration

Next.js (App Router)

// app/layout.tsx
import { KryptosConnectProvider } from "@kryptos_connect/web-sdk";
import "@kryptos_connect/web-sdk/dist/styles/index.css";

const config = {
appName: "My Next.js App",
clientId: process.env.NEXT_PUBLIC_KRYPTOS_CLIENT_ID!,
walletConnectProjectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID!,
theme: "light" as const,
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<KryptosConnectProvider config={config}>
{children}
</KryptosConnectProvider>
</body>
</html>
);
}

React (Vite/CRA)

// main.tsx
import ReactDOM from "react-dom/client";
import { KryptosConnectProvider } from "@kryptos_connect/web-sdk";
import "@kryptos_connect/web-sdk/dist/styles/index.css";
import App from "./App";

const config = {
appName: "My React App",
clientId: import.meta.env.VITE_KRYPTOS_CLIENT_ID,
walletConnectProjectId: import.meta.env.VITE_WALLETCONNECT_PROJECT_ID,
theme: "light" as const,
};

ReactDOM.createRoot(document.getElementById("root")!).render(
<KryptosConnectProvider config={config}>
<App />
</KryptosConnectProvider>
);

Browser Support

  • Chrome/Edge (latest)
  • Firefox (latest)
  • Safari (latest)
  • Mobile browsers (iOS Safari, Chrome Mobile)

Peer Dependencies

  • React >= 16.8.0
  • React DOM >= 16.8.0

Mobile SDK

The Kryptos Connect Mobile SDK provides React Native components for iOS and Android applications. It works with both Expo and React Native CLI.

Installation

npm install @kryptos_connect/mobile-sdk react-native-svg

Core Dependencies

Install all required dependencies:

npx expo install @reown/appkit-react-native @react-native-async-storage/async-storage \
react-native-get-random-values react-native-svg @react-native-community/netinfo \
@walletconnect/react-native-compat react-native-safe-area-context expo-application

Platform Setup

iOS (React Native CLI only):

cd ios && pod install

Expo SDK 53+ - Add to babel.config.js:

module.exports = function (api) {
api.cache(true);
return {
presets: [["babel-preset-expo", { unstable_transformImportMeta: true }]],
};
};

Quick Start

import { KryptosConnectProvider, KryptosConnectButton } from "@kryptos_connect/mobile-sdk";

// 1. Wrap your app with the provider
export default function App() {
return (
<KryptosConnectProvider
config={{
appName: "My Mobile App",
appLogo: "https://yourapp.com/logo.png",
clientId: "your-kryptos-client-id",
theme: "light",
walletConnectProjectId: "your-walletconnect-project-id",
}}
>
<ConnectScreen />
</KryptosConnectProvider>
);
}

// 2. Use the connect button
function ConnectScreen() {
const generateLinkToken = async () => {
const response = await fetch("https://your-api.com/api/kryptos/create-link-token", {
method: "POST",
headers: { "Content-Type": "application/json" },
});
const data = await response.json();
return data.link_token;
};

const handleSuccess = (userConsent) => {
if (userConsent?.public_token) {
// Exchange public token for access token
fetch("https://your-api.com/api/kryptos/exchange-token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ public_token: userConsent.public_token }),
});
}
};

return (
<KryptosConnectButton
generateLinkToken={generateLinkToken}
onSuccess={handleSuccess}
onError={() => console.log("Connection failed")}
>
Connect to Kryptos
</KryptosConnectButton>
);
}

Provider Configuration

OptionTypeRequiredDescription
appNamestringYesYour application name
appLogostring | ReactNodeNoLogo URL or image source
clientIdstringYesKryptos client ID from Developer Portal
theme"light" | "dark"NoUI theme (default: "light")
walletConnectProjectIdstringNoWalletConnect v2 project ID

Button Configuration

OptionTypeRequiredDescription
generateLinkToken() => Promise<string>YesFunction that returns link token
onSuccess(userConsent: UserConsent) => voidNoCallback on successful connection
onError() => voidNoCallback on connection failure
childrenReactNodeNoCustom button content
styleViewStyleNoContainer styling
textStyleTextStyleNoText styling

Custom Button Styling

<KryptosConnectButton
generateLinkToken={generateLinkToken}
onSuccess={handleSuccess}
onError={handleError}
style={{ backgroundColor: '#8b5cf6', borderRadius: 12, padding: 16 }}
textStyle={{ color: '#ffffff', fontSize: 16, fontWeight: 'bold' }}
>
Connect Wallet
</KryptosConnectButton>

Using KryptosConnectModal

For advanced use cases, use the modal component directly:

import { KryptosConnectModal } from "@kryptos_connect/mobile-sdk";
import { useState } from "react";
import { TouchableOpacity, Text } from "react-native";

function ConnectScreen() {
const [open, setOpen] = useState(false);

return (
<>
<TouchableOpacity onPress={() => setOpen(true)}>
<Text>Open Connection Modal</Text>
</TouchableOpacity>
<KryptosConnectModal
open={open}
setOpen={setOpen}
generateLinkToken={generateLinkToken}
onSuccess={handleSuccess}
onError={handleError}
/>
</>
);
}

WalletConnect Integration

To enable WalletConnect v2 functionality, add the required imports at your app entry point:

// App.tsx or index.js - add these imports at the top
import "@walletconnect/react-native-compat";
import "react-native-get-random-values";

import { KryptosConnectProvider } from "@kryptos_connect/mobile-sdk";

const config = {
appName: "Your App",
appLogo: "https://your-logo.png",
clientId: "your-kryptos-client-id",
walletConnectProjectId: "your-walletconnect-project-id", // Required for WalletConnect
};

export default function App() {
return (
<KryptosConnectProvider config={config}>
<YourApp />
</KryptosConnectProvider>
);
}

Platform Requirements

PlatformMinimum Version
iOS12.0+
AndroidAPI 21+ (Android 5.0+)
React Native0.60+
Expo SDK48+

Features

  • Cross-Platform: Single codebase for iOS and Android
  • Expo Support: Works with Expo and React Native CLI
  • WalletConnect v2: Built-in WalletConnect integration
  • Dark Mode: Light and dark theme support
  • TypeScript: Full TypeScript support

Package Information


Backend Implementation

Architecture

┌─────────────┐         ┌─────────────┐         ┌─────────────┐
│ Client │ │ Your │ │ Kryptos │
│ App │ │ Backend │ │ Connect │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
│ 1. Request link token │ │
├──────────────────────►│ │
│ │ 2. Create link token │
│ ├──────────────────────►│
│ │ │
│ │◄──────────────────────┤
│ │ link_token │
│◄──────────────────────┤ │
│ link_token │ │
│ │ │
│ 3. Open widget │ │
├───────────────────────┼──────────────────────►│
│ │ User authenticates │
│ │ and grants consent │
│◄──────────────────────┼───────────────────────┤
│ public_token │ │
│ │ │
│ 4. Exchange token │ │
├──────────────────────►│ │
│ │ 5. Exchange token │
│ ├──────────────────────►│
│ │ │
│ │◄──────────────────────┤
│ │ access_token (15 yr) │
│◄──────────────────────┤ grant_id │
│ Success │ │

Create a link token endpoint on your backend that the Web SDK's generateLinkToken function will call.

Endpoint: POST https://connect-api.kryptos.io/link-token

Authentication: Client credentials via X-Client-Id and X-Client-Secret headers

const express = require("express");
const axios = require("axios");

const app = express();
app.use(express.json());

const KRYPTOS_BASE_URL = "https://connect-api.kryptos.io";

// Endpoint for the Web SDK's generateLinkToken function
app.post("/api/kryptos/create-link-token", async (req, res) => {
try {
// Check if user has an existing access token stored
const existingAccessToken = await getUserAccessToken(req.user?.id);

const payload = {
scopes:
"openid profile offline_access email portfolios:read transactions:read integrations:read tax:read accounting:read reports:read workspace:read users:read",
};

// If user has existing token, include it to skip authentication
if (existingAccessToken) {
payload.access_token = existingAccessToken;
}

const response = await axios.post(
`${KRYPTOS_BASE_URL}/link-token`,
payload,
{
headers: {
"Content-Type": "application/json",
"X-Client-Id": process.env.KRYPTOS_CLIENT_ID,
"X-Client-Secret": process.env.KRYPTOS_CLIENT_SECRET,
},
}
);

// Return format expected by Web SDK's generateLinkToken
res.json({
link_token: response.data.data.link_token,
isAuthorized: !!existingAccessToken, // true = skip auth, false = full flow
});
} catch (error) {
res.status(500).json({ error: "Failed to create link token" });
}
});

Your Backend Response (for Web SDK):

{
"link_token": "link_abc123xyz789",
"isAuthorized": false
}

Kryptos API Response (Fresh Session):

{
"success": true,
"data": {
"link_token": "link_abc123xyz789",
"expires_at": "2024-01-28T10:30:00Z"
}
}

Kryptos API Response (Session Resumed with access_token):

{
"success": true,
"data": {
"link_token": "link_abc123xyz789",
"expires_at": "2024-01-28T10:30:00Z",
"user_id": "uuid-user-123",
"workspace_id": "uuid-workspace-456",
"has_existing_grant": true
}
}

Step 2: Exchange Public Token

Create an endpoint to exchange the public token for a long-lived access token. The Web SDK's onSuccess callback will send the public_token to this endpoint.

Endpoint: POST https://connect-api.kryptos.io/token/exchange

Authentication: Client credentials via headers

// Endpoint for the Web SDK's onSuccess callback
app.post("/api/kryptos/exchange-token", async (req, res) => {
try {
const { public_token } = req.body;

const response = await axios.post(
`${KRYPTOS_BASE_URL}/token/exchange`,
{ public_token },
{
headers: {
"Content-Type": "application/json",
"X-Client-Id": process.env.KRYPTOS_CLIENT_ID,
"X-Client-Secret": process.env.KRYPTOS_CLIENT_SECRET,
},
}
);

const { access_token, grant_id, workspace_id } = response.data.data;

// Store tokens securely for the user
await saveUserTokens(req.user.id, {
access_token,
grant_id,
workspace_id,
});

res.json({ success: true });
} catch (error) {
res.status(500).json({ error: "Failed to exchange token" });
}
});

Response:

{
"success": true,
"data": {
"access_token": "cat_abc123xyz789",
"grant_id": "cgrant_abc123xyz789",
"token_type": "Bearer",
"expires_in": 473040000,
"scope": "openid profile offline_access email portfolios:read transactions:read integrations:read tax:read accounting:read reports:read workspace:read users:read",
"workspace_id": "uuid-workspace-123"
}
}
Long-Lived Access Tokens

Access tokens are valid for 15 years (473,040,000 seconds). No refresh tokens are needed. Store the grant_id to allow users to revoke access later.

Step 3: Make API Calls

Use the access token to call Kryptos APIs:

async function getUserHoldings(accessToken) {
const response = await axios.get(
"https://connect.kryptos.io/api/v1/holdings",
{
headers: {
Authorization: `Bearer ${accessToken}`,
"X-Client-Id": process.env.KRYPTOS_CLIENT_ID,
"X-Client-Secret": process.env.KRYPTOS_CLIENT_SECRET,
},
}
);

return response.data;
}

Session Management

Resume Existing Session

Pass an existing access token when creating a link token to skip the authentication flow for returning users:

async function createLinkTokenWithSession(userId, existingAccessToken) {
const response = await axios.post(
"https://connect-api.kryptos.io/link-token",
{
scopes:
"openid profile offline_access email portfolios:read transactions:read integrations:read tax:read accounting:read reports:read workspace:read users:read",
access_token: existingAccessToken, // Pre-authenticate with existing token
},
{
headers: {
"Content-Type": "application/json",
"X-Client-Id": process.env.KRYPTOS_CLIENT_ID,
"X-Client-Secret": process.env.KRYPTOS_CLIENT_SECRET,
},
}
);

return response.data.data;
}

Response with existing session:

{
"success": true,
"data": {
"link_token": "link_xxx...",
"expires_at": "...",
"user_id": "user123",
"workspace_id": "ws123",
"has_existing_grant": true
}
}

Check Session Status

Check if a user has an active session before opening the widget:

async function checkSession(accessToken) {
const response = await axios.post(
"https://connect-api.kryptos.io/link-token/check-session",
{
access_token: accessToken,
},
{
headers: {
"Content-Type": "application/json",
"X-Client-Id": process.env.KRYPTOS_CLIENT_ID,
"X-Client-Secret": process.env.KRYPTOS_CLIENT_SECRET,
},
}
);

return response.data.data;
}

Response (Valid Session):

{
"success": true,
"data": {
"has_valid_session": true,
"user_id": "uuid-user-123",
"workspace_id": "uuid-workspace-456",
"workspace_name": "My Workspace",
"granted_scopes": "openid profile offline_access email portfolios:read transactions:read integrations:read tax:read accounting:read reports:read workspace:read users:read",
"grant_id": "cgrant_abc123"
}
}

Response (No Valid Session):

{
"success": true,
"data": {
"has_valid_session": false,
"reason": "token_expired"
}
}

Revoke Grant

Allow users to disconnect their account by revoking the grant:

Endpoint: POST https://connect-api.kryptos.io/token/revoke

async function revokeGrant(grantId) {
const response = await axios.post(
"https://connect-api.kryptos.io/token/revoke",
{
grant_id: grantId,
},
{
headers: {
"Content-Type": "application/json",
"X-Client-Id": process.env.KRYPTOS_CLIENT_ID,
"X-Client-Secret": process.env.KRYPTOS_CLIENT_SECRET,
},
}
);

return response.data;
}

Response:

{
"success": true,
"message": "Grant revoked successfully"
}
Grant Revocation

When a grant is revoked, all associated access tokens are immediately invalidated. Any subsequent API calls using those tokens will fail.

List Connected Grants

Get all active grants for your client:

Endpoint: GET https://connect-api.kryptos.io/token/grants

async function listGrants() {
const response = await axios.get(
"https://connect-api.kryptos.io/token/grants",
{
headers: {
"X-Client-Id": process.env.KRYPTOS_CLIENT_ID,
"X-Client-Secret": process.env.KRYPTOS_CLIENT_SECRET,
},
}
);

return response.data.data;
}

Response:

{
"success": true,
"data": {
"grants": [
{
"grant_id": "cgrant_abc123xyz789",
"workspace_id": "uuid-workspace-123",
"scopes": "transactions:read balances:read",
"created_at": "2024-01-15T10:00:00Z",
"expires_at": "2039-01-15T10:00:00Z"
}
],
"count": 1
}
}

Available Scopes

Default Client Scopes

When you create a new OAuth client, the following scopes are assigned by default:

openid profile offline_access email portfolios:read transactions:read
integrations:read tax:read accounting:read reports:read workspace:read users:read

Core Scopes

ScopeDescription
openidRequired for OpenID Connect
profileUser profile information
emailUser email address
offline_accessEnable long-lived tokens

API Scopes

ResourceRead ScopeWrite ScopeDescription
Portfoliosportfolios:readportfolios:writePortfolio holdings
Transactionstransactions:readtransactions:writeTransaction history
Balancesbalances:readbalances:writeAccount balances
Integrationsintegrations:readintegrations:writeConnected wallets and exchanges
DeFidefi:readdefi:writeDeFi protocol positions
NFTnft:readnft:writeNFT collections
Taxtax:readtax:writeTax calculations
Accountingaccounting:readaccounting:writeAccounting ledger
Reportsreports:readreports:writeGenerated reports
Workspaceworkspace:readworkspace:writeWorkspace settings

Error Handling

Common Errors

Error CodeDescriptionSolution
INVALID_CLIENTInvalid client credentialsVerify client_id and client_secret
INVALID_TOKENToken expired or invalidRe-authenticate user
TOKEN_EXPIREDLink or public token expiredCreate new link token
TOKEN_ALREADY_USEDToken was already consumedCreate new link token
INVALID_SCOPERequested scope not allowedCheck allowed scopes for your client
INVALID_GRANTGrant revoked or invalidRe-authenticate user
INSUFFICIENT_PERMISSIONSUser lacks required roleUser must be owner/admin
WORKSPACE_NOT_FOUNDWorkspace doesn't existVerify workspace ID

Error Response Format

{
"success": false,
"error": "Error message",
"code": "ERROR_CODE"
}

Handling Revoked Grants

Since access tokens are long-lived, the grant may be revoked before the token expires. Handle this case:

async function makeApiCallWithCheck(endpoint, accessToken, grantId) {
try {
return await makeApiCall(endpoint, accessToken);
} catch (error) {
if (error.response?.status === 401) {
// Token or grant invalid - user needs to re-authenticate
throw new Error("Access revoked. Please reconnect your account.");
}
throw error;
}
}

Security Best Practices

  1. Never expose secrets in frontend code - Keep client_secret on your backend only
  2. Store tokens securely - Use encrypted storage for access tokens and grant IDs
  3. Use HTTPS - Always use secure connections in production
  4. Validate state parameters - Prevent CSRF attacks
  5. Monitor for suspicious activity - Log and monitor authentication events
  6. Allow users to revoke access - Provide UI to disconnect integrations using /token/revoke
  7. Store grant IDs - Keep grant IDs for revocation purposes

Token Types & Lifetimes

Token TypePrefixLifetimeDescription
Link Tokenlink_30 minutesInitialize Connect widget
Public Tokenpublic_30 minutesExchange for access token (one-time)
Access Tokencat_15 yearsAPI authentication (long-lived)
Grant Tokencgrant_15 yearsAuthorization record (for revocation)

Sandbox Mode

New clients are created in sandbox mode by default, which has the following limitations:

Supported Chains

Only Ethereum and Solana are available in sandbox mode.

Test Addresses

Use these pre-approved test addresses for sandbox testing:

ChainTest Address
Ethereum0x47c2e31e9ce22437bcf6313d2b9e98245a7bfcfa
Solana2r8Hm938GzCQ2gXTP2deDarkn52ezYf1UUaEzMpkwrk1

Sandbox Errors

Error CodeDescription
SANDBOX_PROVIDER_NOT_ALLOWEDProvider not available in sandbox mode
SANDBOX_ADDRESS_NOT_ALLOWEDAddress not allowed in sandbox mode

Production Access

To upgrade to production mode with full access to all chains and addresses, contact us:


Support

SDK Packages

Contact & Resources

GitHub Repositories


Next Steps

  1. Set up OAuth 2.0 - Configure full OAuth flow
  2. Explore API Endpoints - Browse available APIs
  3. View Type Definitions - TypeScript types for API responses