Documentation Index
Fetch the complete documentation index at: https://mintlify.com/blindpaylabs/blindpay-node/llms.txt
Use this file to discover all available pages before exploring further.
Error Response Pattern
BlindPay SDK uses a discriminated union pattern for all API responses. Every SDK method returns a BlindpayApiResponse<T> type that can be either a success or an error:
type BlindpayApiResponse<T> = BlindpaySuccessResponse<T> | BlindpayErrorResponse;
type BlindpaySuccessResponse<T> = {
data: T;
error: null;
};
type BlindpayErrorResponse = {
data: null;
error: {
message: string;
};
};
This pattern provides several benefits:
- Type-safe error handling with TypeScript
- No thrown exceptions to catch
- Explicit success/error checking at each call site
- Consistent pattern across all SDK methods
Basic Error Handling
Every SDK method call should check for errors before using the data:
const { data, error } = await blindpay.receivers.get("re_abc123");
if (error) {
console.error("Failed to get receiver:", error.message);
// Handle the error appropriately
return;
}
// TypeScript knows data is not null here
console.log("Receiver:", data.first_name, data.last_name);
TypeScript Type Narrowing
The discriminated union pattern enables TypeScript to narrow types automatically:
const response = await blindpay.receivers.createIndividualWithStandardKYC({
country: "US",
email: "user@example.com"
});
// response type: BlindpayApiResponse<CreateIndividualWithStandardKYCResponse>
if (response.error) {
// Inside this block, TypeScript knows:
// - response.error is { message: string }
// - response.data is null
console.error(response.error.message);
return;
}
// Outside the error block, TypeScript knows:
// - response.error is null
// - response.data is CreateIndividualWithStandardKYCResponse
console.log("Created receiver:", response.data.id);
This eliminates the need for type assertions or optional chaining.
Error Handling Patterns
Early Return Pattern
The most common pattern - check for errors and return early:
const createPayoutFlow = async (receiverId: string, amount: number) => {
// Step 1: Get the receiver
const { data: receiver, error: receiverError } =
await blindpay.receivers.get(receiverId);
if (receiverError) {
console.error("Failed to get receiver:", receiverError.message);
return { success: false, error: receiverError.message };
}
// Step 2: Get their bank accounts
const { data: accounts, error: accountsError } =
await blindpay.receivers.bankAccounts.list(receiverId);
if (accountsError) {
console.error("Failed to get bank accounts:", accountsError.message);
return { success: false, error: accountsError.message };
}
if (accounts.data.length === 0) {
return { success: false, error: "No bank accounts found" };
}
// Step 3: Create a quote
const { data: quote, error: quoteError } = await blindpay.quotes.create({
bank_account_id: accounts.data[0].id,
currency_type: "receiver",
request_amount: amount,
network: "base",
token: "USDC",
cover_fees: false
});
if (quoteError) {
console.error("Failed to create quote:", quoteError.message);
return { success: false, error: quoteError.message };
}
return { success: true, quote };
};
Throw on Error Pattern
If you prefer exception-based error handling, you can throw errors:
const getReceiverOrThrow = async (receiverId: string) => {
const { data, error } = await blindpay.receivers.get(receiverId);
if (error) {
throw new Error(`Failed to get receiver: ${error.message}`);
}
return data;
};
// Usage
try {
const receiver = await getReceiverOrThrow("re_abc123");
console.log(receiver.email);
} catch (err) {
console.error(err.message);
}
Wrapper Helper Pattern
Create a helper function to handle the response pattern:
function unwrap<T>(response: BlindpayApiResponse<T>): T {
if (response.error) {
throw new Error(response.error.message);
}
return response.data;
}
// Usage
try {
const receiver = unwrap(await blindpay.receivers.get("re_abc123"));
const accounts = unwrap(await blindpay.receivers.bankAccounts.list(receiver.id));
const quote = unwrap(await blindpay.quotes.create({ /* ... */ }));
console.log("Quote created:", quote.id);
} catch (err) {
console.error("Failed:", err.message);
}
Validation Before API Calls
Validate input before making API calls to provide better error messages:
const createQuoteSafely = async (bankAccountId: string, amount: number) => {
// Validate inputs
if (!bankAccountId) {
return {
data: null,
error: { message: "Bank account ID is required" }
};
}
if (amount <= 0) {
return {
data: null,
error: { message: "Amount must be greater than 0" }
};
}
if (amount > 1000000) {
return {
data: null,
error: { message: "Amount exceeds maximum limit" }
};
}
// Make the API call
return await blindpay.quotes.create({
bank_account_id: bankAccountId,
currency_type: "sender",
request_amount: amount,
network: "base",
token: "USDC",
cover_fees: true
});
};
Common Error Scenarios
Invalid Credentials
const blindpay = new BlindPay({
apiKey: "invalid-key",
instanceId: "in_000000000000"
});
const { data, error } = await blindpay.receivers.list();
if (error) {
// error.message: "Unauthorized" or "Invalid API key"
console.error("Authentication failed:", error.message);
}
Resource Not Found
const { data, error } = await blindpay.receivers.get("re_nonexistent");
if (error) {
// error.message: "Receiver not found" or similar
console.error("Receiver not found:", error.message);
}
Validation Errors
const { data, error } = await blindpay.receivers.createIndividualWithStandardKYC({
country: "US",
email: "invalid-email" // Invalid email format
});
if (error) {
// error.message will describe the validation failure
console.error("Validation failed:", error.message);
}
Quote Expired
const { data: quote, error: quoteError } = await blindpay.quotes.create({
bank_account_id: "ba_abc123",
currency_type: "sender",
request_amount: 1000,
network: "base",
token: "USDC",
cover_fees: true
});
if (quoteError) {
console.error("Quote creation failed:", quoteError.message);
return;
}
// Wait too long...
await new Promise(resolve => setTimeout(resolve, 15 * 60 * 1000)); // 15 minutes
// Try to use expired quote
const { data: payout, error: payoutError } = await blindpay.payouts.createEvm({
quote_id: quote.id,
sender_wallet_address: "0x..."
});
if (payoutError) {
// error.message: "Quote has expired" or similar
console.error("Payout failed:", payoutError.message);
// Create a new quote
}
Rate Limits
const { data, error } = await blindpay.receivers.list();
if (error) {
// error.message: "Rate limit exceeded" or similar
console.error("Rate limited:", error.message);
// Implement exponential backoff
}
Network Errors
const { data, error } = await blindpay.receivers.list();
if (error) {
// Network errors are caught and returned as error responses
// error.message: "Network request failed" or similar
console.error("Network error:", error.message);
// Retry with exponential backoff
}
Error Recovery Strategies
Retry with Exponential Backoff
const withRetry = async <T>(
fn: () => Promise<BlindpayApiResponse<T>>,
maxRetries = 3,
baseDelay = 1000
): Promise<BlindpayApiResponse<T>> => {
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fn();
if (response.data) {
return response; // Success
}
lastError = response.error;
// Don't retry on certain errors
if (response.error.message.includes("not found") ||
response.error.message.includes("Unauthorized")) {
return response; // Don't retry
}
// Wait before retrying
if (attempt < maxRetries - 1) {
const delay = baseDelay * Math.pow(2, attempt);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
// Return the last error after all retries
return { data: null, error: lastError! };
};
// Usage
const { data, error } = await withRetry(() =>
blindpay.receivers.get("re_abc123")
);
Fallback Values
const getReceiverWithFallback = async (receiverId: string) => {
const { data, error } = await blindpay.receivers.get(receiverId);
if (error) {
console.error("Failed to get receiver:", error.message);
return {
id: receiverId,
email: "unknown@example.com",
first_name: "Unknown",
last_name: "User"
};
}
return data;
};
User-Friendly Error Messages
const getUserFriendlyError = (error: { message: string }): string => {
const message = error.message.toLowerCase();
if (message.includes("unauthorized") || message.includes("api key")) {
return "Authentication failed. Please check your credentials.";
}
if (message.includes("not found")) {
return "The requested resource could not be found.";
}
if (message.includes("rate limit")) {
return "Too many requests. Please try again in a few minutes.";
}
if (message.includes("network") || message.includes("timeout")) {
return "Network error. Please check your connection and try again.";
}
if (message.includes("expired")) {
return "This quote has expired. Please create a new one.";
}
// Default message
return "An unexpected error occurred. Please try again.";
};
// Usage
const { data, error } = await blindpay.quotes.create({ /* ... */ });
if (error) {
const friendlyMessage = getUserFriendlyError(error);
console.error(friendlyMessage);
// Show to user in UI
}
Logging and Monitoring
Structured Error Logging
const logError = (operation: string, error: { message: string }, context?: any) => {
console.error({
timestamp: new Date().toISOString(),
operation,
error: error.message,
context
});
};
// Usage
const { data, error } = await blindpay.receivers.get(receiverId);
if (error) {
logError("get_receiver", error, { receiverId });
}
Error Tracking Integration
import * as Sentry from '@sentry/node';
const { data, error } = await blindpay.quotes.create({ /* ... */ });
if (error) {
Sentry.captureException(new Error(error.message), {
tags: {
operation: 'create_quote',
service: 'blindpay'
},
extra: {
bank_account_id: bankAccountId,
amount: requestAmount
}
});
}
BlindPayError Class
The SDK also exports a BlindPayError class for client-side validation:
import { BlindPay, BlindPayError } from 'blindpay-node-sdk';
try {
const blindpay = new BlindPay({
apiKey: "", // Empty API key
instanceId: "in_000000000000"
});
} catch (error) {
if (error instanceof BlindPayError) {
console.error("BlindPay initialization error:", error.message);
}
}
The constructor throws BlindPayError if:
- API key is missing or empty
- Instance ID is missing or empty
Best Practices
-
Always check for errors: Never assume API calls succeed. Check the error field on every response.
-
Provide context in error logs: Include relevant IDs, parameters, and state when logging errors.
-
Use type narrowing: Let TypeScript’s type narrowing help you write safer code.
-
Handle errors at the appropriate level: Handle errors close to where they occur, but allow critical errors to bubble up.
-
Implement retry logic for transient errors: Network errors and rate limits can often be resolved with retries.
-
Show user-friendly messages: Transform technical error messages into helpful guidance for end users.
-
Monitor and alert on errors: Track error rates and patterns in your production environment.
-
Test error scenarios: Write tests that cover error cases, not just happy paths.
-
Document error handling: Make sure your team knows how to handle common error scenarios.
-
Validate before API calls: Catch obvious errors before making network requests.