The agentref package is a lightweight, fully-typed Node.js client for the AgentRef REST API v1. It handles authentication, retries, pagination, idempotency, and structured error handling out of the box.
Installation
Quick Start
import { AgentRef } from 'agentref';
const client = new AgentRef({
apiKey: 'ak_live_your_api_key_here',
});
// List all programs
const { data: programs, meta } = await client.programs.list();
console.log(`Found ${meta.total} programs`);
// Get conversion stats
const stats = await client.conversions.stats({ period: '30d' });
console.log(`Revenue: $${stats.totalRevenue / 100}`);
Configuration
const client = new AgentRef({
apiKey: 'ak_live_...', // or set AGENTREF_API_KEY env var
baseUrl: 'https://www.agentref.dev/api/v1', // default
timeout: 30_000, // 30s default
maxRetries: 2, // retries for GET + idempotent POST
});
| Option | Type | Default | Description |
|---|
apiKey | string | AGENTREF_API_KEY env | Your API key. Required. |
baseUrl | string | https://www.agentref.dev/api/v1 | API base URL |
timeout | number | 30000 | Request timeout in ms |
maxRetries | number | 2 | Max retries for safe/idempotent requests |
dangerouslyAllowBrowser | boolean | false | Override browser safety check (not recommended) |
Never expose API keys in client-side code. The SDK throws an error if initialized in a browser context unless you explicitly opt in with dangerouslyAllowBrowser: true.
Resource Namespaces
The client exposes 8 resource namespaces, each mapping to a group of REST API endpoints:
| Namespace | Description |
|---|
client.programs | Create, update, delete, and list affiliate programs |
client.affiliates | List, approve, block, and unblock affiliates |
client.conversions | List conversions, get stats, and fetch recent activity |
client.payouts | List payouts, pending affiliates, stats, and create payouts |
client.flags | List fraud flags, get stats, and resolve flags |
client.billing | Check billing status, list tiers, and subscribe |
client.merchant | Get and update merchant profile and notification preferences |
client.webhooks | Create, list, update, delete webhook endpoints and rotate secrets |
Programs
// List programs (paginated)
const { data, meta } = await client.programs.list({
status: 'active',
limit: 10,
page: 1,
});
// Auto-paginate through all programs
for await (const program of client.programs.listAll()) {
console.log(program.name);
}
// Get program details (includes readiness status)
const detail = await client.programs.get('prog-uuid');
console.log(detail.readiness); // 'setup' | 'partial' | 'ready'
// Create a program
const program = await client.programs.create({
name: 'Acme Referrals',
commissionType: 'recurring',
commissionPercent: 25,
cookieDuration: 60,
currency: 'USD',
}, { idempotencyKey: 'create-acme-v1' });
// Update a program
await client.programs.update('prog-uuid', {
commissionPercent: 30,
status: 'active',
});
// Delete a program
await client.programs.delete('prog-uuid');
// Get program stats
const stats = await client.programs.stats('prog-uuid', { period: '30d' });
// List program affiliates
const affiliates = await client.programs.listAffiliates('prog-uuid', {
includeBlocked: false,
});
// Manage invites
const invite = await client.programs.createInvite('prog-uuid', {
email: 'partner@example.com',
expiresInDays: 7,
}, { idempotencyKey: 'invite-partner-v1' });
const invites = await client.programs.listInvites('prog-uuid');
// Manage coupons
const coupon = await client.programs.createCoupon('prog-uuid', {
affiliateId: 'aff-uuid',
code: 'PARTNER20',
}, { idempotencyKey: 'coupon-partner20' });
const coupons = await client.programs.listCoupons('prog-uuid');
await client.programs.deleteCoupon('coupon-uuid');
// Marketplace settings
await client.programs.updateMarketplace('prog-uuid', {
status: 'public',
category: 'SaaS',
description: 'Earn 25% recurring for every referral.',
});
// Stripe connection
const stripe = await client.programs.connectStripe('prog-uuid');
console.log(stripe.authUrl); // redirect merchant to complete OAuth
await client.programs.disconnectStripe('prog-uuid');
Affiliates
// List all affiliates
const { data } = await client.affiliates.list({
programId: 'prog-uuid',
status: 'pending',
search: 'john',
sortBy: 'totalRevenue',
sortOrder: 'desc',
limit: 25,
});
// Get single affiliate (with optional stats)
const affiliate = await client.affiliates.get('aff-uuid', {
include: 'stats',
});
// Approve
await client.affiliates.approve('aff-uuid', {
idempotencyKey: 'approve-aff-123',
});
// Block with reason
await client.affiliates.block('aff-uuid', {
reason: 'Suspicious click patterns detected',
}, { idempotencyKey: 'block-aff-123' });
// Unblock
await client.affiliates.unblock('aff-uuid', {
idempotencyKey: 'unblock-aff-123',
});
Conversions
// List conversions with filters
const { data, meta } = await client.conversions.list({
programId: 'prog-uuid',
status: 'pending',
startDate: '2026-01-01',
endDate: '2026-03-23',
limit: 50,
});
// Aggregate stats
const stats = await client.conversions.stats({
programId: 'prog-uuid',
period: '30d',
});
console.log(`${stats.total} conversions, $${stats.totalRevenue / 100} revenue`);
// Recent conversions
const recent = await client.conversions.recent({ limit: 5 });
Payouts
// List completed payouts
const { data } = await client.payouts.list({
status: 'completed',
startDate: '2026-01-01',
});
// List pending affiliates (ready for payout)
const pending = await client.payouts.listPending({
programId: 'prog-uuid',
});
for (const aff of pending.data) {
console.log(`${aff.name}: $${aff.pendingAmount / 100} (${aff.currency})`);
}
// Payout stats
const payoutStats = await client.payouts.stats({ period: '30d' });
// Create a payout
const payout = await client.payouts.create({
affiliateId: 'aff-uuid',
programId: 'prog-uuid',
method: 'paypal',
notes: 'March payout',
}, { idempotencyKey: 'payout-march-aff123' });
Fraud Flags
// List open flags
const { data: flags } = await client.flags.list({
status: 'open',
limit: 20,
});
// Get flag stats
const flagStats = await client.flags.stats();
console.log(`${flagStats.open} open flags`);
// Resolve a flag
await client.flags.resolve('flag-uuid', {
status: 'dismissed',
note: 'Verified legitimate traffic',
blockAffiliate: false,
}, { idempotencyKey: 'resolve-flag-abc' });
Billing
// Check current billing status
const billing = await client.billing.current();
console.log(`Tier: ${billing.tier}, Revenue: $${billing.monthlyRevenue / 100}`);
// List available tiers
const tiers = await client.billing.tiers();
// Subscribe to a tier
await client.billing.subscribe({
tier: 'growth',
}, { idempotencyKey: 'subscribe-growth-v1' });
Merchant
// Get merchant profile
const merchant = await client.merchant.get();
// Update merchant settings
await client.merchant.update({
companyName: 'Acme Inc.',
timezone: 'America/New_York',
defaultCookieDuration: 60,
});
// Notification preferences
const prefs = await client.merchant.getNotifications();
await client.merchant.updateNotifications({
newAffiliate: true,
newConversion: true,
weeklyDigest: true,
});
// Payout info
const payoutInfo = await client.merchant.getPayoutInfo();
await client.merchant.updatePayoutInfo({
payoutMethod: 'paypal',
paypalEmail: 'payments@acme.com',
});
Webhooks
// List webhook endpoints
const endpoints = await client.webhooks.list({
programId: 'prog-uuid',
});
// Create endpoint
const { endpoint, signingSecret } = await client.webhooks.create({
name: 'Production Webhook',
url: 'https://api.acme.com/webhooks/agentref',
subscribedEvents: [
'conversion.created',
'payout.completed',
'affiliate.joined',
],
programId: 'prog-uuid',
});
// Store signingSecret securely -- it's shown only once
// Update endpoint
await client.webhooks.update('wh-uuid', {
subscribedEvents: [
'conversion.created',
'conversion.refunded',
'payout.completed',
],
});
// Rotate signing secret
const { signingSecret: newSecret } = await client.webhooks.rotateSecret('wh-uuid');
// Delete endpoint
await client.webhooks.delete('wh-uuid');
All list endpoints return a PaginatedResponse<T>:
interface PaginatedResponse<T> {
data: T[];
meta: {
total: number;
page: number;
pageSize: number;
hasMore: boolean;
nextCursor?: string;
requestId: string;
};
}
const page1 = await client.programs.list({ page: 1, limit: 25 });
if (page1.meta.hasMore) {
const page2 = await client.programs.list({ page: 2, limit: 25 });
}
The listAll() generator handles pagination automatically:
for await (const program of client.programs.listAll({ pageSize: 100 })) {
console.log(program.name);
}
Idempotency
All mutation methods (POST) accept an idempotencyKey option. When provided, the request is safe to retry — the server guarantees at-most-once execution.
const program = await client.programs.create({
name: 'Acme Referrals',
commissionType: 'recurring',
commissionPercent: 25,
}, {
idempotencyKey: 'create-acme-referrals-v1',
});
The SDK automatically retries failed requests (up to maxRetries) for:
- GET/HEAD requests — always safe to retry
- POST requests with an idempotency key — server-side deduplication
- 429 (rate limited) and 5xx (server error) responses
POST requests without an idempotency key are never retried.
Error Handling
The SDK throws typed errors for all API failures:
import {
AgentRefError,
AuthError,
ForbiddenError,
ValidationError,
NotFoundError,
ConflictError,
RateLimitError,
ServerError,
} from 'agentref';
try {
await client.programs.get('nonexistent-id');
} catch (error) {
if (error instanceof NotFoundError) {
console.log(`Not found: ${error.message}`);
console.log(`Request ID: ${error.requestId}`);
} else if (error instanceof RateLimitError) {
console.log(`Rate limited. Retry after ${error.retryAfter}s`);
} else if (error instanceof ValidationError) {
console.log(`Validation failed: ${error.message}`);
console.log('Details:', error.details);
} else if (error instanceof AgentRefError) {
console.log(`API error ${error.status}: ${error.code} - ${error.message}`);
}
}
| Error Class | HTTP Status | When |
|---|
AuthError | 401 | Invalid or missing API key |
ForbiddenError | 403 | Key lacks required scope |
ValidationError | 400 | Invalid request parameters |
NotFoundError | 404 | Resource does not exist |
ConflictError | 409 | Duplicate resource or state conflict |
RateLimitError | 429 | Too many requests |
ServerError | 5xx | Server-side error |
All errors extend AgentRefError and include code, status, message, and requestId properties.
TypeScript Types
All types are exported from the package:
import type {
Program,
ProgramDetail,
ProgramStats,
Affiliate,
Conversion,
ConversionStats,
Payout,
PendingAffiliate,
Flag,
FlagStats,
BillingStatus,
Merchant,
WebhookEndpoint,
PaginatedResponse,
CreateProgramParams,
UpdateProgramParams,
ResolveFlagParams,
CreatePayoutParams,
CreateWebhookEndpointParams,
// ... and more
} from 'agentref';