> ## Documentation Index
> Fetch the complete documentation index at: https://agentref.co/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Node.js SDK

> Official Node.js SDK for the AgentRef REST API. Manage programs, affiliates, conversions, payouts, fraud flags, billing, webhooks, and merchant settings from server-side JavaScript or TypeScript.

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

```bash theme={null}
npm install agentref
```

## Quick Start

```typescript theme={null}
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

```typescript theme={null}
const client = new AgentRef({
  apiKey: 'ak_live_...',       // or set AGENTREF_API_KEY env var
  baseUrl: 'https://www.agentref.co/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.co/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) |

<Warning>
  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`.
</Warning>

## Resource Namespaces

The client exposes the full REST API v1 surface through resource namespaces:

| Namespace                   | Description                                                                   |
| --------------------------- | ----------------------------------------------------------------------------- |
| `client.programs`           | Create, update, delete, and list affiliate programs                           |
| `client.applications`       | Review pending affiliate applications before they become memberships          |
| `client.affiliates`         | List approved memberships, block, and unblock affiliates                      |
| `client.affiliateWorkspace` | Affiliate self-serve overview, programs, links, earnings, payouts, and clicks |
| `client.marketingResources` | Merchant and affiliate Marketing Resources workflows                          |
| `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                                               |
| `client.notifications`      | Get and update notification preferences                                       |
| `client.payoutInfo`         | Get and update affiliate payout details                                       |
| `client.onboarding`         | Complete onboarding setup calls                                               |
| `client.tracking`           | Read program tracking health                                                  |
| `client.invites`            | Inspect and claim public invite links                                         |
| `client.marketplace`        | Discover marketplace programs and apply as an affiliate                       |
| `client.webhooks`           | Create, list, update, delete webhook endpoints and rotate secrets             |

***

## Programs

```typescript theme={null}
// 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');
```

## Applications

```typescript theme={null}
// Pending joins live in the Applications lifecycle
const pending = await client.applications.list({
  programId: 'prog-uuid',
  status: 'pending',
});

await client.applications.approve(
  pending.data[0]!.id,
  { note: 'Relevant SaaS audience.' },
  { idempotencyKey: 'approve-app-123' }
);

await client.applications.decline('app-uuid', {
  note: 'Audience is not a fit.',
});
```

## Affiliates

```typescript theme={null}
// List approved or blocked affiliate memberships
const { data } = await client.affiliates.list({
  programId: 'prog-uuid',
  status: 'approved',
  search: 'john',
  sortBy: 'totalRevenue',
  sortOrder: 'desc',
  limit: 25,
});

// Get single affiliate (with optional stats)
const affiliate = await client.affiliates.get('aff-uuid', {
  include: 'stats',
});

// 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',
});
```

## Affiliate Workspace

```typescript theme={null}
const identity = await client.affiliateWorkspace.identity();
const overview = await client.affiliateWorkspace.overview();
const programs = await client.affiliateWorkspace.listPrograms();

const link = await client.affiliateWorkspace.createLink(
  {
    name: 'Pricing',
    destinationPath: '/pricing',
    customSlug: 'jane-review',
  },
  {
    programId: programs[0]!.programId,
    idempotencyKey: 'create-pricing-link',
  }
);

const links = await client.affiliateWorkspace.listLinks({
  programId: programs[0]!.programId,
});

await client.affiliateWorkspace.updateLink(
  link.id,
  {
    name: 'Pricing - updated',
    targetUrl: 'https://merchant-site.com/pricing',
    isActive: true,
  },
  { programId: programs[0]!.programId }
);

await client.affiliateWorkspace.deleteLink('old-link-uuid', {
  programId: programs[0]!.programId,
});

const earnings = await client.affiliateWorkspace.earnings();
const programEarnings = await client.affiliateWorkspace.listProgramEarnings(
  programs[0]!.programId,
  { period: '30d' }
);
const payouts = await client.affiliateWorkspace.listPayouts({
  programId: programs[0]!.programId,
});
const clicks = await client.affiliateWorkspace.clickStats({
  programId: programs[0]!.programId,
  startDate: '2026-05-01',
});
```

<Info>
  `createLink` uses `destinationPath` and `customSlug`. The current update API accepts `name`, `targetUrl`, and `isActive`; create a new link when you need a different destination path or custom slug.
</Info>

## Marketing Resources

```typescript theme={null}
// Merchant workflow
const post = await client.marketingResources.createSocialPost(
  'prog-uuid',
  {
    title: 'Launch copy',
    body: 'Try AgentRef: {{affiliate_link}}',
    platforms: ['linkedin'],
    status: 'published',
  },
  { idempotencyKey: 'mr-launch-copy' }
);

await client.marketingResources.publish(
  post.id,
  { programId: 'prog-uuid', notifyAffiliates: true },
  { idempotencyKey: 'publish-mr-launch-copy' }
);

await client.marketingResources.updateSocialPost(post.id, {
  programId: 'prog-uuid',
  title: 'Launch copy v2',
});

const upload = await client.marketingResources.createSocialPostMediaUploadSession(
  post.id,
  {
    programId: 'prog-uuid',
    fileName: 'launch.png',
    mimeType: 'image/png',
    fileSizeBytes: 125_000,
  },
  { idempotencyKey: 'launch-media-upload' }
);

// Upload the file bytes to upload.signed_url, then attach the uploaded media.
await client.marketingResources.completeSocialPostMediaUpload(
  post.id,
  {
    programId: 'prog-uuid',
    uploadPath: upload.upload_path,
    uploadToken: upload.upload_token,
    fileName: 'launch.png',
    altText: 'Launch product screenshot',
  },
  { idempotencyKey: 'launch-media-complete' }
);

await client.marketingResources.createDownloadUrl(
  { programId: 'prog-uuid', resourceId: post.id },
  { idempotencyKey: 'download-launch-copy' }
);

// Affiliate workflow
const resources = await client.marketingResources.listForAffiliate('prog-uuid', {
  kind: 'social_posts',
});

const resource = await client.marketingResources.getForAffiliate(resources[0]!.id, {
  programId: 'prog-uuid',
});

const rendered = await client.marketingResources.renderSocialPost(resources[0]!.id, {
  programId: 'prog-uuid',
  affiliateLinkId: link.id,
});

const download = await client.marketingResources.createAffiliateDownloadUrl({
  programId: 'prog-uuid',
  resourceId: resource.id,
});
```

REST/SDK Marketing Resources support social posts, social-post media, publish/unpublish/archive/notify actions, and download URLs. Collection creation, external-link creation, generic file upload sessions, and URL import are currently available through Merchant MCP, not through the REST SDK.

| Method                                                                                          | Surface            |
| ----------------------------------------------------------------------------------------------- | ------------------ |
| `list`, `createSocialPost`, `updateSocialPost`                                                  | Merchant REST/SDK  |
| `createSocialPostMediaUploadSession`, `completeSocialPostMediaUpload`, `replaceSocialPostMedia` | Merchant REST/SDK  |
| `reorderSocialPostMedia`, `updateSocialPostMedia`, `removeSocialPostMedia`                      | Merchant REST/SDK  |
| `publish`, `unpublish`, `archive`, `notifyAffiliates`, `createDownloadUrl`                      | Merchant REST/SDK  |
| `listForAffiliate`, `getForAffiliate`, `renderSocialPost`, `createAffiliateDownloadUrl`         | Affiliate REST/SDK |
| Collections, external links, generic file upload, URL import                                    | Merchant MCP only  |

## Conversions

```typescript theme={null}
// 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

```typescript theme={null}
// 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

```typescript theme={null}
// 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

```typescript theme={null}
// 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

```typescript theme={null}
// 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.notifications.get();
await client.notifications.update({
  newAffiliate: true,
  newConversion: true,
  weeklyDigest: true,
});

// Payout info
const payoutInfo = await client.payoutInfo.get();
await client.payoutInfo.update({
  payoutMethod: 'paypal',
  paypalEmail: 'payments@acme.com',
});
```

## Onboarding, Tracking, Invites, and Marketplace

```typescript theme={null}
await client.onboarding.upsertMerchantProfile({
  companyName: 'Acme Inc.',
});
await client.onboarding.complete();

const tracking = await client.tracking.getProgramStatus('prog-uuid');

const invite = await client.invites.get('invite-token');
const claimed = await client.invites.claim(invite.token);

const marketplace = await client.marketplace.listPrograms({
  sort: 'commission',
  minCommission: 20,
});

await client.marketplace.apply(marketplace.data[0]!.programId!, {
  message: 'I manage affiliate agents for SaaS products.',
});
```

The REST SDK onboarding namespace currently covers merchant profile upsert by `companyName` and onboarding completion. Merchant MCP has additional setup tools for `get_onboarding_status`, `get_tracking_snippet`, and active tracking verification.

## Webhooks

```typescript theme={null}
// 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');
```

***

## Pagination

Paginated REST list methods return a `PaginatedResponse<T>`:

```typescript theme={null}
interface PaginatedResponse<T> {
  data: T[];
  meta: {
    total: number;
    page: number;
    pageSize: number;
    hasMore: boolean;
    nextCursor?: string;
    requestId: string;
  };
}
```

Some convenience list methods return plain arrays because their REST endpoints return array data directly. Examples include `client.affiliateWorkspace.listPrograms()`, `client.affiliateWorkspace.listLinks()`, `client.marketingResources.list()`, `client.marketingResources.listForAffiliate()`, `client.webhooks.list()`, and `client.conversions.recent()`.

### Page-based pagination

```typescript theme={null}
const page1 = await client.programs.list({ page: 1, limit: 25 });
if (page1.meta.hasMore) {
  const page2 = await client.programs.list({ page: 2, limit: 25 });
}
```

### Auto-pagination

The `listAll()` generator handles pagination automatically:

```typescript theme={null}
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.

```typescript theme={null}
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:

```typescript theme={null}
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:

```typescript theme={null}
import type {
  Program,
  ProgramDetail,
  ProgramStats,
  Application,
  AffiliateLink,
  MarketingResource,
  RenderedMarketingSocialPost,
  Affiliate,
  Conversion,
  ConversionStats,
  Payout,
  PendingAffiliate,
  Flag,
  FlagStats,
  BillingStatus,
  Merchant,
  WebhookEndpoint,
  PaginatedResponse,
  CreateProgramParams,
  UpdateProgramParams,
  ResolveFlagParams,
  CreatePayoutParams,
  CreateWebhookEndpointParams,
  // ... and more
} from 'agentref';
```
