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:

  1. Request an invite to Payman.
  2. Once approved, you’ll receive an invite code.
  3. Use the invite code to register on the Payman Dashboard.
  4. Navigate to the Developers section and register your application.

Step 2: Get Your Credentials

After registering your app, Payman provides two sets of credentials:

  • Test Credentials (auto-created):

    • Works with test wallets (TSD) and test payees
    • You’ll also get:
      • A TSD test wallet (with 1000 TSD)
      • A default test payee
      • A default policy for spend control
  • Live Credentials (approval required):

    • Enables real payments with USD and USDC wallets
    • Requires KYC and policy approval

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.withClientCredentials({
	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");

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:

  1. Redirecting users to a Payman consent screen
  2. Users grant specific permissions (scopes)
  3. Your app receives an authorization code
  4. You exchange the code for an access token

OAuth Configuration Requirements

When registering an app for OAuth, you must provide:

FieldDescription
PurposeWhy your app needs access
Redirect URIsWhere the user is redirected after granting access (max 5)
Origin DomainsAllowed domains for your frontend (max 5)
ScopesWhich permissions you’re requesting
Transaction LimitsMaximum allowed per transaction

Available OAuth Scopes

Each scope grants your app specific capabilities:

ScopePermission
read_balanceView wallet balances
read_list_walletsView list of connected wallets
read_list_payeesView all existing payees
read_list_transactionsView wallet transaction history
write_create_payeeCreate a new payee
write_send_paymentSend money to an existing payee
write_create_walletProgrammatically create a new wallet

Only request the scopes you actually need.


Integrating OAuth

Step 1: Add the Payman Connect Button

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_wallets,read_list_payees,read_list_transactions,write_create_payee,write_send_payment,write_create_wallet"
	data-redirect-uri="https://your-app.com/oauth/callback"
	data-target="#payman-connect"
	data-dark-mode="false"
	data-styles='{"borderRadius": "8px", "fontSize": "14px"}'></script>
AttributeDescriptionRequired
data-client-idYour client IDYes
data-scopesRequested scopesYes
data-redirect-uriRedirect after authYes
data-targetWhere to inject buttonYes
data-dark-modeDark mode toggleNo
strategyScript loading strategyNo
data-stylesCustom stylesNo

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("your-client-id", {
		accessToken,
		expiresIn,
	});
	const payeesResponse = await client.ask("list all payees");
	const balanceResponse = await 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" });
	}
});

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("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]"
);

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.withClientCredentials({
	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");

Best Practices

  1. Use test mode in development
  2. Never expose client secrets in frontend code
  3. Always handle OAuth errors gracefully
  4. Store and refresh tokens securely
  5. Request only the scopes you need
  6. Use env variables for secrets
  7. Monitor token expiry
  8. Cache tokens appropriately

Need help? Reach out to [email protected]