Why Use OAuth?
Payman’s OAuth system allows your application to securely request and manage access to wallets, payees, and transactions — either for your own account or on behalf of your users.
It supports both:
- Client Credentials Flow — for apps that automate actions using your own Payman resources (e.g. agent-based automation or backend apps).
- Authorization Code Flow — for apps that connect to end users’ wallets via OAuth, with granular permissions and full user consent.
Whether you’re building a backend integration or a third-party app, OAuth ensures safe, scoped, and revocable access to financial actions.
Getting Started
Step 1: Request Developer Access
To begin using OAuth:
- Request an invite to Payman.
- Once approved, you’ll receive an invite code.
- Use the invite code to register on the Payman Dashboard.
- Navigate to the Developers section and register your application.
Step 2: Get Your Credentials
After registering your app, Payman provides two sets of credentials:
Authentication Methods
Payman supports 3 OAuth-based authentication strategies depending on your use case:
1. Client Credentials Flow (Machine-to-Machine)
This method is ideal for backend apps or AI agents that need to access your own resources (e.g., wallets or payees created under your account).
You’ll use your app’s Client ID and Client Secret to request a token.
import { PaymanClient } from "@paymanai/payman-ts";
const client = PaymanClient.withCredentials({
clientId: "your-client-id",
clientSecret: "your-client-secret",
});
const walletResponse = await client.ask("list all wallets and their balances");
const createPayeeResponse = await client.ask(
"create a new payee named John Doe with email [email protected]"
);
const paymentResponse = await client.ask("send 100 TSD to John Doe");
const transactionsResponse = await client.ask("show my last 5 transactions");
import { PaymanClient } from "@paymanai/payman-ts";
const client = PaymanClient.withCredentials({
clientId: "your-client-id",
clientSecret: "your-client-secret",
});
const walletResponse = await client.ask("list all wallets and their balances");
const createPayeeResponse = await client.ask(
"create a new payee named John Doe with email [email protected]"
);
const paymentResponse = await client.ask("send 100 TSD to John Doe");
const transactionsResponse = await client.ask("show my last 5 transactions");
from payman_sdk.client import PaymanClient
from payman_sdk.types import PaymanConfig
config = {
'client_id': 'your-client-id',
'client_secret': 'your-client-secret',
}
client = PaymanClient.with_credentials(config)
wallet_response = client.ask("list all wallets and their balances")
create_payee_response = client.ask("create a new payee named John Doe with email [email protected]")
payment_response = client.ask("send 100 TSD to John Doe")
transactions_response = client.ask("show my last 5 transactions")
2. Authorization Code Flow (OAuth with End Users)
Use this when your app needs to access another user’s wallets or payees via OAuth.
It involves:
- Redirecting users to a Payman consent screen
- Users grant specific permissions (scopes)
- Your app receives an authorization code
- You exchange the code for an access token
OAuth Configuration Requirements
When registering an app for OAuth, you must provide:
Field | Description |
---|
Purpose | Why your app needs access |
Redirect URIs | Where the user is redirected after granting access (max 5) |
Origin Domains | Allowed domains for your frontend (max 5) - Make sure you add https://app.paymanai.com/ as one of the originating domains |
Scopes | Which permissions you’re requesting |
Transaction Limits | Maximum allowed per transaction |
Available OAuth Scopes
Each scope grants your app specific capabilities:
Scope | Permission |
---|
read_balance | View wallet balances |
read_list_wallets | View list of connected wallets |
read_list_payees | View all existing payees |
read_list_transactions | View wallet transaction history |
write_create_payee | Create a new payee |
write_send_payment | Send money to an existing payee |
write_create_wallet | Programmatically create a new wallet |
Only request the scopes you actually need.
Integrating OAuth
Use the following script to add a customizable OAuth connect button to your site or app.
This button launches the OAuth consent flow. Users are prompted to grant access to their Payman wallets and approve your requested scopes.
<div id="payman-connect"></div>
<script
src="https://app.paymanai.com/js/pm.js"
data-client-id="your-client-id"
data-scopes="read_balance,read_list_payees,read_list_transactions,write_create_payee"
data-redirect-uri="https://your-app.com/oauth/callback"
data-target="#payman-connect"
data-dark-mode="false"
strategy="afterInteractive"
data-styles='{"borderRadius": "8px", "fontSize": "14px"}'></script>
=======
Add more data-scopes as needed, always separate them with commas without spaces.
Attribute | Description | Required |
---|
data-client-id | Your client ID | Yes |
data-scopes | Requested scopes | Yes |
data-redirect-uri | Redirect after auth | Yes |
data-target | Where to inject button | Yes |
data-dark-mode | Dark mode toggle | No |
strategy | Script loading strategy | No |
data-styles | Custom styles | No |
Step 2: Handle OAuth Callback (Frontend)
Once the user authorizes access, Payman will redirect back to your specified redirect_uri
with an authorization code
.
Use the provided code snippet to:
- Extract the code from the redirect URL
- Send it to your backend for secure token exchange
⚠️ Security Note: Never exchange OAuth codes in frontend code. Always pass them to your backend to handle token exchange securely using your client secret.
window.addEventListener("message", function (event) {
if (event.data.type === "payman-oauth-redirect") {
const url = new URL(event.data.redirectUri);
const code = url.searchParams.get("code");
exchangeCodeForToken(code);
}
});
async function exchangeCodeForToken(code: string) {
const response = await fetch("/api/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ code }),
});
const { accessToken, expiresIn } = await response.json();
const client = PaymanClient.withToken(
{ clientId: "your-client-id" },
{ accessToken, expiresIn }
);
const payeesResponse = await client.ask("list all payees");
const balanceResponse = await client.ask("what's my wallet balance?");
}
window.addEventListener("message", function (event) {
if (event.data.type === "payman-oauth-redirect") {
const url = new URL(event.data.redirectUri);
const code = url.searchParams.get("code");
exchangeCodeForToken(code);
}
});
async function exchangeCodeForToken(code: string) {
const response = await fetch("/api/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ code }),
});
const { accessToken, expiresIn } = await response.json();
const client = PaymanClient.withToken(
{ clientId: "your-client-id" },
{ accessToken, expiresIn }
);
const payeesResponse = await client.ask("list all payees");
const balanceResponse = await client.ask("what's my wallet balance?");
}
# Frontend: Extract the code from the redirect URL and send to backend
# Backend: Exchange the code for a token (see next tab for backend example)
# After receiving accessToken and expiresIn from your backend:
from payman_sdk.client import PaymanClient
client_id = "your-client-id"
token_info = {
'accessToken': 'your-access-token', # from backend
'expiresIn': 3600, # from backend
}
client = PaymanClient.with_token(client_id, token_info)
payees_response = client.ask("list all payees")
balance_response = client.ask("what's my wallet balance?")
Step 3: Token Exchange (Backend)
Your server will exchange the auth code for an access token using the client credentials.
This token gives your app permission to act on the user’s behalf for the scopes granted.
✅ Tokens expire — always track the expiresIn
and refresh if needed.
import express from "express";
import { PaymanClient } from "@paymanai/payman-ts";
const app = express();
app.use(express.json());
app.post("/api/oauth/token", async (req, res) => {
try {
const { code } = req.body;
const client = PaymanClient.withAuthCode(
{
clientId: process.env.PAYMAN_CLIENT_ID,
clientSecret: process.env.PAYMAN_CLIENT_SECRET,
},
code
);
const tokenResponse = await client.getAccessToken();
res.json({
accessToken: tokenResponse.accessToken,
expiresIn: tokenResponse.expiresIn,
});
} catch (error) {
console.error("Token exchange failed:", error);
res.status(500).json({ error: "Token exchange failed" });
}
});
import express from "express";
import { PaymanClient } from "@paymanai/payman-ts";
const app = express();
app.use(express.json());
app.post("/api/oauth/token", async (req, res) => {
try {
const { code } = req.body;
const client = PaymanClient.withAuthCode(
{
clientId: process.env.PAYMAN_CLIENT_ID,
clientSecret: process.env.PAYMAN_CLIENT_SECRET,
},
code
);
const tokenResponse = await client.getAccessToken();
res.json({
accessToken: tokenResponse.accessToken,
expiresIn: tokenResponse.expiresIn,
});
} catch (error) {
console.error("Token exchange failed:", error);
res.status(500).json({ error: "Token exchange failed" });
}
});
from flask import Flask, request, jsonify
from payman_sdk.client import PaymanClient
from payman_sdk.types import PaymanConfig
import os
app = Flask(__name__)
@app.route("/api/oauth/token", methods=["POST"])
def exchange_token():
try:
code = request.json.get("code")
config = {
'client_id': os.environ.get('PAYMAN_CLIENT_ID'),
'client_secret': os.environ.get('PAYMAN_CLIENT_SECRET'),
}
client = PaymanClient.with_auth_code(config, code)
token_response = client.get_access_token()
return jsonify({
'accessToken': token_response.get('accessToken'),
'expiresIn': token_response.get('expiresIn'),
})
except Exception as e:
print("Token exchange failed:", e)
return jsonify({"error": "Token exchange failed"}), 500
Method 3: Direct Access Token Method
This is useful in mobile development environments like React Native, or if you’ve already obtained an access token through other means (e.g. Playground, backend exchange).
You simply initialize the SDK using the existing token.
🚫 Warning: Never hardcode or expose access tokens in frontend web apps. Use environment variables or secure storage.
const client = PaymanClient.withToken(
{ clientId: "your-client-id" },
{ accessToken: "your-access-token", expiresIn: 3600 }
);
const response = await client.ask("list all wallets");
const transactions = await client.ask("show my last 10 transactions");
const createPayee = await client.ask(
"create a new payee named Jane Smith with email [email protected]"
);
const client = PaymanClient.withToken(
{ clientId: "your-client-id" },
{ accessToken: "your-access-token", expiresIn: 3600 }
);
const response = await client.ask("list all wallets");
const transactions = await client.ask("show my last 10 transactions");
const createPayee = await client.ask(
"create a new payee named Jane Smith with email [email protected]"
);
from payman_sdk.client import PaymanClient
client_id = "your-client-id"
token_info = {
'accessToken': 'your-access-token',
'expiresIn': 3600,
}
client = PaymanClient.with_token(client_id, token_info)
response = client.ask("list all wallets")
transactions = client.ask("show my last 10 transactions")
create_payee = client.ask("create a new payee named Jane Smith with email [email protected]")
Testing Your Integration
Use your test credentials during development:
- Simulate requests with test wallets (TSD)
- Use Playground in the Dashboard to validate your app setup before building
- Perform OAuth flows using a test Payman account
Testing Snippets
const testClient = PaymanClient.withCredentials({
clientId: "pm-test-your-test-client-id",
clientSecret: "your-test-client-secret",
});
const wallets = await testClient.ask("list all wallets");
const payee = await testClient.ask(
"create a new payee named Test User with email [email protected]"
);
const payment = await testClient.ask("send 50 TSD to Test User");
const testClient = PaymanClient.withCredentials({
clientId: "pm-test-your-test-client-id",
clientSecret: "your-test-client-secret",
});
const wallets = await testClient.ask("list all wallets");
const payee = await testClient.ask(
"create a new payee named Test User with email [email protected]"
);
const payment = await testClient.ask("send 50 TSD to Test User");
from payman_sdk.client import PaymanClient
from payman_sdk.types import PaymanConfig
config = {
'client_id': 'pm-test-your-test-client-id',
'client_secret': 'your-test-client-secret',
}
test_client = PaymanClient.with_credentials(config)
wallets = test_client.ask("list all wallets")
payee = test_client.ask("create a new payee named Test User with email [email protected]")
payment = test_client.ask("send 50 TSD to Test User")
Best Practices
- Use test mode in development
- Never expose client secrets in frontend code
- Always handle OAuth errors gracefully
- Store and refresh tokens securely
- Request only the scopes you need
- Use env variables for secrets
- Monitor token expiry
- Cache tokens appropriately