# Commissions
Source: https://agentref.co/docs/affiliates/commissions
How affiliate commissions are calculated and tracked.
## How Commissions Work
When someone you referred makes a purchase, you earn a commission. The amount depends on the program's commission structure.
## Commission Types
| Type | How It Works | Example |
| ------------------------ | ---------------------------------------------------------------- | ---------------------------- |
| **One-time** | Percentage commission on the initial purchase only | 30% of a $99 sale = $29.70 |
| **Recurring** | Percentage commission on each subscription renewal | 20% of every monthly renewal |
| **Recurring with limit** | Recurring percentage commission capped to a set number of months | 20% for the first 12 months |
The commission type and rate are set by the merchant for each program.
## Commission Lifecycle
Every commission goes through these stages:
```
Conversion Created → Pending → Approved → Paid
```
| Status | Description |
| -------------------- | --------------------------------------------------------------------- |
| `pending` | Conversion recorded, waiting for approval period |
| `approved` | Commission confirmed, waiting for payout threshold |
| `paid` | Included in a payout, money sent to you |
| `partially_refunded` | Customer received a partial refund, commission reduced proportionally |
| `refunded` | Customer fully refunded, commission reversed |
### Why Pending?
Commissions start as **pending** to protect against refunds and fraud. The approval period varies by program – typically 14-30 days. This is standard practice in affiliate marketing.
## Viewing Your Commissions
### Dashboard
Your affiliate dashboard shows:
* Total commissions earned (all time)
* Pending commissions (not yet approved)
* Available for payout (approved, waiting for threshold)
* Commission history with per-conversion detail
### Via API
Use `GET /me/earnings` for the cross-program overview and `GET /me/earnings/{programId}` when you want line-item detail for one program.
```javascript Node.js theme={null}
const headers = {
Authorization: 'Bearer ak_aff_YOUR_KEY',
};
const overview = await fetch('https://www.agentref.co/api/v1/me/earnings', { headers }).then((r) => r.json());
const details = await fetch(
'https://www.agentref.co/api/v1/me/earnings/PROGRAM_UUID?period=30d',
{ headers }
).then((r) => r.json());
```
```python Python theme={null}
import httpx
headers = {"Authorization": "Bearer ak_aff_YOUR_KEY"}
overview = httpx.get("https://www.agentref.co/api/v1/me/earnings", headers=headers).json()
details = httpx.get(
"https://www.agentref.co/api/v1/me/earnings/PROGRAM_UUID",
headers=headers,
params={"period": "30d"},
).json()
```
```bash cURL theme={null}
curl "https://www.agentref.co/api/v1/me/earnings" \
-H "Authorization: Bearer ak_aff_YOUR_KEY"
curl "https://www.agentref.co/api/v1/me/earnings/PROGRAM_UUID?period=30d" \
-H "Authorization: Bearer ak_aff_YOUR_KEY"
```
## Recurring Commissions
Some programs offer **recurring commissions** on subscription products. If a customer you referred stays subscribed, you earn a commission on each renewal – not just the first payment.
Whether a program offers one-time or recurring commissions is set by the merchant. Check the program details when you join.
## Refunds
If a customer receives a partial refund, the commission is reduced proportionally and the conversion can move to `partially_refunded`. If the customer receives a full refund, the conversion status changes to `refunded` and the commission is reversed.
Refunded commissions that were already paid out are deducted from your next payout.
# Affiliate FAQ
Source: https://agentref.co/docs/affiliates/faq
Frequently asked questions about being an affiliate on AgentRef.
## Getting Started
Either click an invite link from a merchant, or browse the [AgentRef Marketplace](https://www.agentref.co/marketplace) and apply to programs that interest you.
No. Joining as an affiliate is completely free. AgentRef charges the merchant, not the affiliate.
Yes. You can be an affiliate for as many programs as you want. Each program has its own referral code, commission rate, and payout settings.
No. You just need to share your referral link. If you're a developer or AI agent, you can also use the API to automate your workflow.
## Tracking & Attribution
When someone clicks your referral link, a first-party cookie is set on their browser. If they make a purchase within the cookie window (set by the merchant, typically 30-90 days), the conversion is attributed to you.
Cookie-based tracking is per-browser. If a visitor clicks your link on mobile but purchases on desktop, the conversion won't be automatically attributed. This is a limitation of cookie-based tracking across the industry.
Yes. Use sub-IDs (`sub1`-`sub5`) in your links to track different placements, and view the breakdown in your dashboard.
## Commissions & Payouts
Once your approved commissions reach the payout threshold (default: \$50), the merchant can create a payout and send the money to your saved payout method. Timing depends on the merchant's payout cadence and the external payment provider.
New commissions start as pending to allow for refunds and fraud review. The approval period is set by the merchant (typically 14-30 days). Once approved, commissions count toward your payout threshold.
The commission is reversed. If it was already paid out, the amount is deducted from your next payout.
AgentRef supports all currencies that Stripe supports. Your commission is earned in the same currency as the merchant's product. Stripe handles currency conversion when paying out to your bank.
You do not connect a Stripe account for affiliate payouts today. Go to **Settings → Payouts** and save your payout details instead, using either PayPal or bank transfer.
## Account & API
Yes. Create an affiliate API key (`ak_aff_*`) in your dashboard under **Settings → API Keys**. Use the [REST API](/docs/api-reference/authentication) for `/me/*` endpoints or the [MCP Server](/docs/agent-integration/mcp-server) for affiliate tools like link generation and earnings lookups.
Yes – AgentRef is agent-compatible. AI agents can join programs, manage links, review click stats, and monitor earnings through the REST API or [MCP Server](/docs/agent-integration/mcp-server).
Contact the merchant who runs the program. Referral code changes are managed by the merchant to prevent tracking disruptions.
# Getting Paid
Source: https://agentref.co/docs/affiliates/getting-paid
How and when affiliate payouts are processed.
## Payout Process
Once your approved commissions reach the payout threshold, the merchant can create a payout in AgentRef, send the money externally, and mark the payout status as it progresses.
Today this is a manual merchant workflow. AgentRef tracks payout state and payout details, but does not automatically send affiliate funds.
### How It Works
As your referrals convert, commissions move from `pending` → `approved`.
When your approved balance reaches the program's payout threshold (default: \$50), you become payout-eligible.
The merchant creates a payout record, sends the money outside AgentRef (for example via PayPal or bank transfer), and records the status in AgentRef.
Once the merchant marks the payout as completed, it appears in your payout history. Arrival time depends on the external payment provider.
## Setting Your Payout Details
To receive payouts smoothly, add your payout details in AgentRef:
1. Go to **Settings → Payouts** in your affiliate dashboard
2. Choose your payout method: `paypal` or `bank_transfer`
3. Enter the required payout details
4. Save the profile so merchants can pay you correctly
Merchants may hold payouts until your payout details are complete. Add them before your first payout becomes due.
## Payout Schedule
| Setting | Default |
| ---------------- | ----------------------- |
| Payout threshold | \$50 USD |
| Processing time | Merchant-defined |
| Payment method | PayPal or bank transfer |
The merchant can configure the payout threshold per program.
## Viewing Payout History
### Dashboard
Your dashboard shows all payouts with status, amount, and date.
### Via API
Use `GET /me/payout-info` to check the payout method on file and `GET /me/payouts` to review payout history.
```javascript Node.js theme={null}
const headers = {
Authorization: 'Bearer ak_aff_YOUR_KEY',
};
const payoutInfo = await fetch('https://www.agentref.co/api/v1/me/payout-info', { headers }).then((r) => r.json());
const payouts = await fetch('https://www.agentref.co/api/v1/me/payouts', { headers }).then((r) => r.json());
```
```python Python theme={null}
import httpx
headers = {"Authorization": "Bearer ak_aff_YOUR_KEY"}
payout_info = httpx.get("https://www.agentref.co/api/v1/me/payout-info", headers=headers).json()
payouts = httpx.get("https://www.agentref.co/api/v1/me/payouts", headers=headers).json()
```
```bash cURL theme={null}
curl "https://www.agentref.co/api/v1/me/payout-info" \
-H "Authorization: Bearer ak_aff_YOUR_KEY"
curl "https://www.agentref.co/api/v1/me/payouts" \
-H "Authorization: Bearer ak_aff_YOUR_KEY"
```
## Payout Statuses
| Status | Meaning |
| ------------ | -------------------------------------------------- |
| `pending` | Payout created, waiting to be processed |
| `processing` | Merchant started the external payment step |
| `completed` | Merchant confirmed the payout as paid |
| `failed` | External payment attempt failed and can be retried |
## Common Issues
The most common reasons are incomplete payout details or a failed external payment attempt. Go to **Settings → Payouts** and confirm your PayPal or bank transfer details are correct.
Check if your approved balance has reached the payout threshold. Also confirm your payout profile is complete and ask the merchant which payout cadence they use.
If a customer requested a refund, the associated commission is reversed. Refunds on already-paid commissions are deducted from your next payout.
# Getting Started as an Affiliate
Source: https://agentref.co/docs/affiliates/getting-started
How to join an affiliate program on AgentRef and start earning commissions.
This guide is for **affiliates** – people who promote products and earn commissions on successful referrals.
## How to Join a Program
There are two ways to join an affiliate program on AgentRef:
### 1. Invite Link
If a merchant sent you an invite link, click it to create your affiliate account. You'll be asked to:
1. Enter your name and email
2. Set your preferred referral code (e.g., `jane`, `techreviewer`)
3. Accept the program terms
Depending on the program settings, you'll either be approved immediately or placed in a pending queue for the merchant to review.
### 2. Marketplace
Browse the [AgentRef Marketplace](https://www.agentref.co/marketplace) to discover programs:
1. Browse available programs
2. Click **Apply** on a program that interests you
3. Fill out the application form
4. Wait for the merchant to approve your application
## Your Affiliate Dashboard
Once approved, you'll have access to your affiliate dashboard where you can:
* **View your referral links** – unique URLs to share with your audience
* **Track clicks** – see how many people clicked your links
* **Monitor conversions** – see which clicks converted into sales
* **View commissions** – track your earnings in real-time
* **Use marketing resources** – download merchant-approved assets and render social posts with your link
* **Check payout status** – see when you'll get paid
## Your API Key
If you're an AI agent or developer, you can access your affiliate data programmatically:
1. Go to **Settings → API Keys** in your affiliate dashboard
2. Create a new API key (prefix: `ak_aff_`)
3. Use it with the REST API or [MCP Server](/docs/agent-integration/mcp-server)
The most useful starting endpoints are:
* `GET /me` for your affiliate identity
* `GET /me/overview` for aggregate earnings and program stats
* `GET /me/programs` for the programs you belong to
* `GET /me/clicks` for click stats over a date range
* `GET /me/programs/{programId}/marketing-resources` for merchant-published campaign material
```javascript Node.js theme={null}
const headers = {
Authorization: 'Bearer ak_aff_YOUR_KEY',
};
const me = await fetch('https://www.agentref.co/api/v1/me', { headers }).then((r) => r.json());
const overview = await fetch('https://www.agentref.co/api/v1/me/overview', { headers }).then((r) => r.json());
const programs = await fetch('https://www.agentref.co/api/v1/me/programs', { headers }).then((r) => r.json());
const clicks = await fetch(
'https://www.agentref.co/api/v1/me/clicks?startDate=START_DATE&endDate=END_DATE',
{ headers }
).then((r) => r.json());
```
```python Python theme={null}
import httpx
headers = {"Authorization": "Bearer ak_aff_YOUR_KEY"}
me = httpx.get("https://www.agentref.co/api/v1/me", headers=headers).json()
overview = httpx.get("https://www.agentref.co/api/v1/me/overview", headers=headers).json()
programs = httpx.get("https://www.agentref.co/api/v1/me/programs", headers=headers).json()
clicks = httpx.get(
"https://www.agentref.co/api/v1/me/clicks",
headers=headers,
params={"startDate": "START_DATE", "endDate": "END_DATE"},
).json()
```
```bash cURL theme={null}
curl "https://www.agentref.co/api/v1/me" \
-H "Authorization: Bearer ak_aff_YOUR_KEY"
curl "https://www.agentref.co/api/v1/me/overview" \
-H "Authorization: Bearer ak_aff_YOUR_KEY"
curl "https://www.agentref.co/api/v1/me/programs" \
-H "Authorization: Bearer ak_aff_YOUR_KEY"
curl "https://www.agentref.co/api/v1/me/clicks?startDate=START_DATE&endDate=END_DATE" \
-H "Authorization: Bearer ak_aff_YOUR_KEY"
```
## What's Next
Learn how tracking links work
Use merchant-provided campaign assets
Understand how you earn
How and when you get paid
Common questions answered
# Links & Attribution
Source: https://agentref.co/docs/affiliates/links-attribution
How affiliate tracking links work and how clicks are attributed to you.
## Your Referral Link
Every affiliate gets a unique referral code. Your tracking link follows this format:
```
https://merchant-site.com/?via=YOUR_CODE
```
AgentRef-generated links default to `?via=YOUR_CODE`. Some programs may still expose `ref`, `r`, `a`, or merchant-configured aliases for inbound attribution.
When someone clicks your link, AgentRef:
1. Records the click with your affiliate ID
2. Sets a first-party cookie on the visitor's browser
3. Tracks the visitor across the cookie window (typically 30-90 days)
4. Attributes any conversion back to you
## Referral Code
Your referral code is set when you join a program. It's usually your name or brand:
```
https://example.com/?via=jane
https://example.com/?via=techreviewer
https://example.com/?via=ai-tools-blog
```
## Creating links via API
Affiliate API keys can create named tracking links for a joined program. Use `destination_path` for a merchant-approved path on the merchant website and `custom_slug` when you want a specific referral code for that link.
`destination_path` must be a relative path such as `/pricing` or `/blog/review`. It must be in the program's allowed destination list. `custom_slug` must be 3-32 lowercase letters, numbers, or hyphens.
```bash cURL theme={null}
curl -X POST "https://www.agentref.co/api/v1/me/links?program_id=PROGRAM_ID" \
-H "Authorization: Bearer ak_aff_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-pricing-link" \
-d '{
"name": "Pricing page",
"destination_path": "/pricing",
"custom_slug": "jane-review"
}'
```
```text MCP theme={null}
create_link({
program_id: "PROGRAM_ID",
name: "Pricing page",
destination_path: "/pricing",
custom_slug: "jane-review",
idempotency_key: "create-pricing-link"
})
```
## Updating links via API
The current update contract is narrower than the create contract. `PATCH /api/v1/me/links/{id}` accepts `name`, `targetUrl`, and `isActive`; it does not accept `destination_path` or `custom_slug`. Use `targetUrl` when you need to point an existing link at a full URL, and create a new link when you need a different approved destination path or custom slug.
```bash cURL theme={null}
curl -X PATCH "https://www.agentref.co/api/v1/me/links/LINK_ID?program_id=PROGRAM_ID" \
-H "Authorization: Bearer ak_aff_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Pricing page - updated",
"targetUrl": "https://merchant-site.com/pricing",
"isActive": true
}'
```
```javascript Node.js theme={null}
await client.affiliateWorkspace.updateLink(
'LINK_ID',
{
name: 'Pricing page - updated',
targetUrl: 'https://merchant-site.com/pricing',
isActive: true,
},
{ programId: 'PROGRAM_ID' }
);
```
```python Python theme={null}
client.affiliate_workspace.update_link(
"LINK_ID",
name="Pricing page - updated",
target_url="https://merchant-site.com/pricing",
is_active=True,
program_id="PROGRAM_ID",
)
```
## Cookie Duration
Each program sets a **cookie window** – the number of days a click stays valid. If a visitor clicks your link today and purchases within the window, you get credit.
| Typical Windows | Duration |
| --------------- | -------- |
| Short | 30 days |
| Standard | 60 days |
| Long | 90 days |
The merchant configures this per program.
## UTM & Sub-ID Tracking
You can append tracking parameters to your links for your own analytics:
```
https://example.com/?via=jane&utm_source=youtube&utm_campaign=review
https://example.com/?via=jane&sub1=header-banner&sub2=homepage
```
### Supported Parameters
| Parameter | Description |
| --------------- | --------------------------------------------- |
| `utm_source` | Traffic source (youtube, twitter, newsletter) |
| `utm_medium` | Medium (video, post, email) |
| `utm_campaign` | Campaign name |
| `utm_content` | Content variant |
| `utm_term` | Search term |
| `sub1` - `sub5` | Custom sub-IDs for your own tracking |
These are tracked per click and visible in your affiliate dashboard.
## Attribution Model
AgentRef uses **last-click attribution**: the last affiliate link clicked before conversion gets the commission. If a visitor clicks affiliate A's link, then later clicks affiliate B's link and purchases – affiliate B gets credit.
## Viewing Your Clicks
### In the Dashboard
Your dashboard shows click analytics: total clicks, unique visitors, click-through rates, and geographic breakdown.
### Via API
Use the affiliate self-serve clicks endpoint to inspect a specific reporting window:
```javascript Node.js theme={null}
const stats = await fetch(
'https://www.agentref.co/api/v1/me/clicks?startDate=START_DATE&endDate=END_DATE',
{
headers: { Authorization: 'Bearer ak_aff_YOUR_KEY' },
}
).then((r) => r.json());
```
```python Python theme={null}
import httpx
stats = httpx.get(
"https://www.agentref.co/api/v1/me/clicks",
headers={"Authorization": "Bearer ak_aff_YOUR_KEY"},
params={"startDate": "START_DATE", "endDate": "END_DATE"},
).json()
```
```bash cURL theme={null}
curl "https://www.agentref.co/api/v1/me/clicks?startDate=START_DATE&endDate=END_DATE" \
-H "Authorization: Bearer ak_aff_YOUR_KEY"
```
Use sub-IDs (`sub1`-`sub5`) to track which content, placement, or campaign drives the most conversions. This helps you optimize your promotion strategy.
# Marketing Resources
Source: https://agentref.co/docs/affiliates/marketing-resources
Use merchant-provided files, social posts, swipe copy, and links in your affiliate promotions.
Marketing Resources are affiliate-ready assets published by a merchant. They can include files, social post templates, swipe copy, external links, and campaign collections.
You only see resources for programs where you are an approved affiliate.
## What you can access
| Resource | How to use it |
| -------------- | ----------------------------------------------------------------------- |
| Files | Download images, PDFs, videos, or other campaign assets. |
| Social posts | Render post copy with your own affiliate link and referral code. |
| Swipe copy | Copy approved messaging for emails, posts, or landing pages. |
| External links | Open merchant-hosted folders, brand kits, or campaign pages. |
| Collections | Download or browse a grouped set of resources for a launch or campaign. |
## Surface coverage
Affiliate REST, SDK, and MCP access is read-oriented. Affiliates can list published resources, inspect a resource, render social post templates, and create download URLs. Creating or editing merchant resources is a merchant-only workflow.
| Capability | Dashboard | REST API | Node/Python SDKs | Affiliate MCP |
| --------------------------------------------- | --------- | -------- | ---------------- | ------------- |
| List published resources for a joined program | Yes | Yes | Yes | Yes |
| Get resource detail | Yes | Yes | Yes | Yes |
| Render social post placeholders | Yes | Yes | Yes | Yes |
| Create download URLs | Yes | Yes | Yes | Yes |
| Create or edit resources | No | No | No | No |
Marketing Resources do not currently emit webhook events. Merchants publish or unpublish resources, and affiliates read the current published state.
## Rendered social posts
Merchants can publish social post templates with placeholders:
| Placeholder | Rendered value |
| -------------------- | ----------------------------------- |
| `{{affiliate_link}}` | Your tracking link for the program. |
| `{{affiliate_code}}` | Your referral code. |
| `{{program_name}}` | The program name. |
When you render a social post, AgentRef returns the final copy, available links, attached media, merchant instructions, and disclosure guidance.
If you share a post with your affiliate link, include a clear disclosure where people can see it. Examples include `#ad`, `affiliate link`, or equivalent wording that fits the platform and local rules.
## REST API
Affiliate Marketing Resources require an affiliate API key (`ak_aff_*`) with `marketing_resources:read`.
| Action | Endpoint |
| ---------------------------- | ----------------------------------------------------------------------- |
| List resources for a program | `GET /api/v1/me/programs/{programId}/marketing-resources` |
| Get resource detail | `GET /api/v1/me/marketing-resources/{resourceId}?program_id=PROGRAM_ID` |
| Render social post | `POST /api/v1/me/marketing-resources/social-posts/{resourceId}/render` |
| Create download URL | `POST /api/v1/me/marketing-resources/download-url` |
```bash List resources theme={null}
curl "https://www.agentref.co/api/v1/me/programs/PROGRAM_ID/marketing-resources?kind=social_posts" \
-H "Authorization: Bearer ak_aff_YOUR_KEY"
```
```bash Render a social post theme={null}
curl -X POST "https://www.agentref.co/api/v1/me/marketing-resources/social-posts/RESOURCE_ID/render" \
-H "Authorization: Bearer ak_aff_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"program_id": "PROGRAM_ID",
"affiliate_link_id": "LINK_ID"
}'
```
## MCP tools
Affiliate MCP exposes Marketing Resources through:
`list_marketing_resources`, `get_marketing_resource`, `render_marketing_social_post`, and `create_marketing_resource_download_url`.
Use the affiliate MCP endpoint:
```text theme={null}
https://www.agentref.co/api/mcp/affiliate
```
## What's next
Create and use tracking links.
Understand how earnings are calculated.
# Agent Patterns
Source: https://agentref.co/docs/agent-integration/agent-patterns
End-to-end workflow examples for AI agents using AgentRef. Complete code for setting up programs, monitoring conversions, handling fraud, and generating payout reports.
This page provides ready-to-use patterns for common agent workflows. Each example shows the complete tool sequence or SDK code an AI agent would use, with both Node.js and Python implementations.
## Pattern 1: Set Up a Complete Affiliate Program
This workflow creates a program, connects Stripe, checks readiness, publishes marketplace settings, and invites the first affiliates -- everything a merchant needs to go live from the REST/SDK surface.
```typescript Node.js theme={null}
import { AgentRef } from 'agentref';
const client = new AgentRef({ apiKey: process.env.AGENTREF_API_KEY });
// Step 1: Create the program
const program = await client.programs.create({
name: 'Acme Pro Referrals',
commissionType: 'recurring',
commissionPercent: 25,
cookieDuration: 60,
payoutThreshold: 5000, // $50.00 in cents
autoApproveAffiliates: true,
currency: 'USD',
}, { idempotencyKey: 'setup-acme-pro-v1' });
console.log(`Program created: ${program.id}`);
// Step 2: Connect Stripe
const stripe = await client.programs.connectStripe(program.id);
console.log(`Complete Stripe OAuth at: ${stripe.authUrl}`);
// The merchant opens this URL in a browser to connect their Stripe account
// Step 3: Check readiness
const detail = await client.programs.get(program.id);
console.log(`Readiness: ${detail.readiness}`);
// Step 4: Publish to marketplace
await client.programs.updateMarketplace(program.id, {
status: 'public',
category: 'SaaS',
description: 'Earn 25% recurring commission on every referral.',
});
// Step 5: Invite affiliates
const invite = await client.programs.createInvite(program.id, {
email: 'top-affiliate@example.com',
name: 'Top Affiliate',
expiresInDays: 14,
}, { idempotencyKey: 'invite-top-affiliate-v1' });
console.log(`Invite sent: ${invite.token}`);
// Step 6: Create a public invite link for broader distribution
const publicInvite = await client.programs.createInvite(program.id, {
isPublic: true,
usageLimit: 100,
expiresInDays: 30,
}, { idempotencyKey: 'public-invite-v1' });
```
```python Python theme={null}
from agentref import AgentRef
client = AgentRef() # reads AGENTREF_API_KEY from env
# Step 1: Create the program
program = client.programs.create(
name="Acme Pro Referrals",
commission_type="recurring",
commission_percent=25,
cookie_duration=60,
payout_threshold=5000, # $50.00 in cents
auto_approve_affiliates=True,
currency="USD",
idempotency_key="setup-acme-pro-v1",
)
print(f"Program created: {program.id}")
# Step 2: Connect Stripe
stripe = client.programs.connect_stripe(program.id)
print(f"Complete Stripe OAuth at: {stripe.auth_url}")
# Step 3: Check readiness
detail = client.programs.get(program.id)
print(f"Readiness: {detail.readiness}")
# Step 4: Publish to marketplace
client.programs.update_marketplace(
program.id,
status="public",
category="SaaS",
description="Earn 25% recurring commission on every referral.",
)
# Step 5: Invite affiliates
invite = client.programs.create_invite(
program.id,
email="top-affiliate@example.com",
name="Top Affiliate",
expires_in_days=14,
idempotency_key="invite-top-affiliate-v1",
)
print(f"Invite sent: {invite.token}")
# Step 6: Public invite link
public_invite = client.programs.create_invite(
program.id,
is_public=True,
usage_limit=100,
expires_in_days=30,
idempotency_key="public-invite-v1",
)
```
***
## Pattern 2: Monitor Conversions and Flag Fraud
This workflow checks recent conversions, reviews fraud flags, and takes action on suspicious activity. Ideal for a scheduled agent task.
```typescript Node.js theme={null}
import { AgentRef } from 'agentref';
const client = new AgentRef({ apiKey: process.env.AGENTREF_API_KEY });
// Step 1: Check conversion stats for the last 7 days
const stats = await client.conversions.stats({ period: '7d' });
console.log(`Last 7 days: ${stats.total} conversions, $${stats.totalRevenue / 100} revenue`);
console.log(`Pending: ${stats.pending}, Approved: ${stats.approved}`);
// Step 2: List recent conversions that need review
const { data: conversions } = await client.conversions.list({
status: 'pending',
limit: 50,
});
console.log(`${conversions.length} pending conversions to review`);
// Step 3: Check for open fraud flags
const flagStats = await client.flags.stats();
console.log(`Open fraud flags: ${flagStats.open}`);
if (flagStats.open > 0) {
const { data: flags } = await client.flags.list({
status: 'open',
limit: 20,
});
for (const flag of flags) {
console.log(`Flag ${flag.id}: ${flag.type} for affiliate ${flag.affiliateId}`);
// Step 4: Auto-dismiss low-risk flags, escalate high-risk
if (flag.type === 'high_click_frequency') {
// Get the affiliate's full stats for context
const affiliate = await client.affiliates.get(flag.affiliateId, {
include: 'stats',
});
if (affiliate.totalConversions > 0) {
// Has real conversions -- likely legitimate traffic
await client.flags.resolve(flag.id, {
status: 'dismissed',
note: `Auto-dismissed: ${affiliate.totalConversions} conversions confirm legitimate traffic`,
blockAffiliate: false,
}, { idempotencyKey: `resolve-${flag.id}` });
} else {
// No conversions -- suspicious, confirm the flag
await client.flags.resolve(flag.id, {
status: 'confirmed',
note: 'Zero conversions with high click volume -- confirmed suspicious',
blockAffiliate: true,
}, { idempotencyKey: `resolve-${flag.id}` });
}
}
}
}
```
```python Python theme={null}
from agentref import AgentRef
client = AgentRef()
# Step 1: Check conversion stats for the last 7 days
stats = client.conversions.stats(period="7d")
print(f"Last 7 days: {stats.total} conversions, ${stats.total_revenue / 100} revenue")
print(f"Pending: {stats.pending}, Approved: {stats.approved}")
# Step 2: List recent conversions that need review
result = client.conversions.list(status="pending", limit=50)
print(f"{len(result.data)} pending conversions to review")
# Step 3: Check for open fraud flags
flag_stats = client.flags.stats()
print(f"Open fraud flags: {flag_stats.open}")
if flag_stats.open > 0:
flags_result = client.flags.list(status="open", limit=20)
for flag in flags_result.data:
print(f"Flag {flag.id}: {flag.type} for affiliate {flag.affiliate_id}")
# Step 4: Auto-dismiss low-risk flags, escalate high-risk
if flag.type == "high_click_frequency":
affiliate = client.affiliates.get(flag.affiliate_id, include="stats")
if affiliate.total_conversions > 0:
client.flags.resolve(
flag.id,
status="dismissed",
note=f"Auto-dismissed: {affiliate.total_conversions} conversions confirm legitimate traffic",
block_affiliate=False,
idempotency_key=f"resolve-{flag.id}",
)
else:
client.flags.resolve(
flag.id,
status="confirmed",
note="Zero conversions with high click volume -- confirmed suspicious",
block_affiliate=True,
idempotency_key=f"resolve-{flag.id}",
)
```
***
## Pattern 3: Generate Weekly Payout Report
This workflow lists pending payouts, aggregates stats, and generates a report. Useful for weekly cron jobs or scheduled agent tasks.
```typescript Node.js theme={null}
import { AgentRef } from 'agentref';
const client = new AgentRef({ apiKey: process.env.AGENTREF_API_KEY });
// Step 1: Get payout stats
const payoutStats = await client.payouts.stats({ period: '30d' });
console.log(`Total paid (30d): $${payoutStats.totalPaid / 100}`);
console.log(`Total pending: $${payoutStats.totalPending / 100}`);
// Step 2: List all pending affiliates across programs
const pending = await client.payouts.listPending();
const report = {
generatedAt: new Date().toISOString(),
totalPending: pending.meta.total,
affiliates: pending.data.map(aff => ({
name: aff.name ?? aff.email,
program: aff.programName,
amount: `$${aff.pendingAmount / 100}`,
currency: aff.currency,
method: aff.payoutMethod ?? 'Not set',
meetsThreshold: aff.meetsThreshold,
commissionCount: aff.commissionCount,
})),
};
console.log('--- Weekly Payout Report ---');
console.log(JSON.stringify(report, null, 2));
// Step 3: For affiliates who meet the threshold and have a payout method,
// create the payout
const eligible = pending.data.filter(
aff => aff.meetsThreshold && aff.hasPayoutMethod
);
console.log(`\n${eligible.length} affiliates eligible for payout`);
for (const aff of eligible) {
const payout = await client.payouts.create({
affiliateId: aff.affiliateId,
programId: aff.programId,
method: aff.payoutMethod!,
notes: `Weekly payout - ${new Date().toISOString().split('T')[0]}`,
}, { idempotencyKey: `weekly-payout-${aff.affiliateId}-${Date.now()}` });
console.log(`Created payout for ${aff.name}: $${aff.pendingAmount / 100}`);
}
// Step 4: Get recent completed payouts for the report footer
const { data: recentPayouts } = await client.payouts.list({
status: 'completed',
limit: 10,
});
console.log(`\nRecent completed payouts: ${recentPayouts.length}`);
```
```python Python theme={null}
from datetime import datetime
from agentref import AgentRef
client = AgentRef()
# Step 1: Get payout stats
payout_stats = client.payouts.stats(period="30d")
print(f"Total paid (30d): ${payout_stats.total_paid / 100}")
print(f"Total pending: ${payout_stats.total_pending / 100}")
# Step 2: List all pending affiliates across programs
pending = client.payouts.list_pending()
report = {
"generated_at": datetime.now().isoformat(),
"total_pending": pending.meta.total,
"affiliates": [
{
"name": aff.name or aff.email,
"program": aff.program_name,
"amount": f"${aff.pending_amount / 100}",
"currency": aff.currency,
"method": aff.payout_method or "Not set",
"meets_threshold": aff.meets_threshold,
"commission_count": aff.commission_count,
}
for aff in pending.data
],
}
print("--- Weekly Payout Report ---")
import json
print(json.dumps(report, indent=2))
# Step 3: Create payouts for eligible affiliates
eligible = [
aff for aff in pending.data
if aff.meets_threshold and aff.has_payout_method
]
print(f"\n{len(eligible)} affiliates eligible for payout")
today = datetime.now().strftime("%Y-%m-%d")
for aff in eligible:
payout = client.payouts.create(
affiliate_id=aff.affiliate_id,
program_id=aff.program_id,
method=aff.payout_method,
notes=f"Weekly payout - {today}",
idempotency_key=f"weekly-payout-{aff.affiliate_id}-{today}",
)
print(f"Created payout for {aff.name}: ${aff.pending_amount / 100}")
# Step 4: Recent completed payouts
recent = client.payouts.list(status="completed", limit=10)
print(f"\nRecent completed payouts: {len(recent.data)}")
```
***
## Idempotency Patterns for Safe Agent Retries
AI agents may retry operations due to timeouts, network failures, or tool-calling loops. Idempotency keys ensure these retries are safe.
### Deterministic Keys
Generate idempotency keys from the operation's intent, not random values:
```typescript Node.js theme={null}
// Good: deterministic key based on intent
const key = `create-program-${programName}-${Date.now()}`;
// Better: based on a stable external identifier
const key = `invite-${email}-to-${programId}`;
// Best: includes a version for intentional re-runs
const key = `setup-acme-program-v2`;
```
```python Python theme={null}
# Good: deterministic key based on intent
key = f"create-program-{program_name}-{int(time.time())}"
# Better: based on a stable external identifier
key = f"invite-{email}-to-{program_id}"
# Best: includes a version for intentional re-runs
key = "setup-acme-program-v2"
```
### Retry-Safe Workflows
When chaining multiple operations, use a unique key for each step:
```typescript Node.js theme={null}
const workflowId = 'onboard-acme-2026-03';
// Each step gets its own scoped key
const program = await client.programs.create({
name: 'Acme Referrals',
commissionType: 'recurring',
commissionPercent: 25,
}, { idempotencyKey: `${workflowId}-create-program` });
await client.programs.createInvite(program.id, {
email: 'partner@example.com',
}, { idempotencyKey: `${workflowId}-invite-partner` });
// Review pending applications through the Applications API or MCP review_application tool.
// Use a separate key such as `${workflowId}-approve-application-${applicationId}`.
```
```python Python theme={null}
workflow_id = "onboard-acme-2026-03"
# Each step gets its own scoped key
program = client.programs.create(
name="Acme Referrals",
commission_type="recurring",
commission_percent=25,
idempotency_key=f"{workflow_id}-create-program",
)
client.programs.create_invite(
program.id,
email="partner@example.com",
idempotency_key=f"{workflow_id}-invite-partner",
)
# Review pending applications through the Applications API or MCP review_application tool.
# Use a separate key such as f"{workflow_id}-approve-application-{application_id}".
```
***
## Error Handling Patterns
### Graceful Degradation
When an agent encounters an error, it should attempt recovery before giving up:
```typescript Node.js theme={null}
import { AgentRef, NotFoundError, RateLimitError, AgentRefError } from 'agentref';
const client = new AgentRef();
async function safeGetProgram(id: string) {
try {
return await client.programs.get(id);
} catch (error) {
if (error instanceof NotFoundError) {
// Program doesn't exist -- list all programs and pick the first
const { data } = await client.programs.list({ status: 'active', limit: 1 });
return data[0] ?? null;
}
if (error instanceof RateLimitError) {
// Wait and retry
await new Promise(r => setTimeout(r, error.retryAfter * 1000));
return await client.programs.get(id);
}
throw error;
}
}
async function safeResolveFlag(flagId: string) {
try {
await client.flags.resolve(flagId, {
status: 'reviewed',
note: 'Reviewed by automated agent',
}, { idempotencyKey: `auto-review-${flagId}` });
return { success: true };
} catch (error) {
if (error instanceof AgentRefError) {
return {
success: false,
error: error.code,
message: error.message,
requestId: error.requestId,
};
}
throw error;
}
}
```
```python Python theme={null}
from agentref import AgentRef, NotFoundError, RateLimitError, AgentRefError
import time
client = AgentRef()
def safe_get_program(program_id: str):
try:
return client.programs.get(program_id)
except NotFoundError:
# Program doesn't exist -- list all and pick the first
result = client.programs.list(status="active", limit=1)
return result.data[0] if result.data else None
except RateLimitError as e:
# Wait and retry
time.sleep(e.retry_after)
return client.programs.get(program_id)
def safe_resolve_flag(flag_id: str):
try:
client.flags.resolve(
flag_id,
status="reviewed",
note="Reviewed by automated agent",
idempotency_key=f"auto-review-{flag_id}",
)
return {"success": True}
except AgentRefError as e:
return {
"success": False,
"error": e.code,
"message": str(e),
"request_id": e.request_id,
}
```
### Logging and Observability
Always capture the `requestId` from errors for debugging:
```typescript theme={null}
try {
await client.conversions.list({ status: 'invalid_status' });
} catch (error) {
if (error instanceof AgentRefError) {
console.error(`AgentRef error [${error.requestId}]: ${error.code} - ${error.message}`);
// Send to your observability platform
sentry.captureException(error, {
extra: { requestId: error.requestId, code: error.code },
});
}
}
```
# CLI
Source: https://agentref.co/docs/agent-integration/cli
The AgentRef CLI is a thin, automation-friendly command-line interface for managing affiliate programs, listing marketplace offerings, and querying account information.
The `agentref` CLI is a lightweight command-line tool that wraps the AgentRef REST API v1 and MCP server. It is designed for scripting, CI/CD pipelines, and quick account operations from the terminal.
The CLI reuses existing AgentRef surfaces -- REST API v1 for account/program operations and MCP for marketplace access. It does not duplicate business logic; all domain logic stays on the server.
## Running the CLI
From the AgentRef repository:
```bash theme={null}
./bin/agentref help
```
Or via npm:
```bash theme={null}
npm run agentref -- help
```
## Authentication
The CLI resolves credentials in this order:
1. `--api-key` flag (highest priority)
2. `AGENTREF_API_KEY` environment variable
3. `~/.agentref/config.json` file
### Config File
Store persistent configuration at `~/.agentref/config.json`:
```json title="~/.agentref/config.json" theme={null}
{
"baseUrl": "https://www.agentref.co",
"apiKey": "ak_live_your_api_key_here",
"updatedAt": "2026-03-23T10:00:00Z"
}
```
If no `--base-url`, `AGENTREF_API_BASE_URL`, saved config, or `NEXT_PUBLIC_APP_URL` is present, the local development CLI falls back to `http://localhost:3000`.
Create API keys in the AgentRef dashboard under **Settings > API Keys**. Keys are shown only once -- store them securely.
## Global Options
Every command accepts these flags:
| Flag | Description |
| ------------------- | --------------------------------------- |
| `--json` | Stable machine-readable output envelope |
| `--base-url ` | Override API base URL |
| `--api-key ` | Override stored API key |
| `--timeout-ms ` | Request timeout in milliseconds |
| `--retries ` | Retry count for retryable failures |
### JSON Output
When `--json` is passed, all commands return a consistent envelope:
```json theme={null}
{
"ok": true,
"command": "programs list",
"transport": "rest",
"data": {
"programs": []
},
"meta": {
"page": 1,
"limit": 20
}
}
```
This is useful for piping into `jq` or other automation tools.
## Commands
### `whoami`
Display the authenticated user identity.
```bash theme={null}
agentref whoami
```
### `merchant me`
Show full merchant account details.
```bash theme={null}
agentref merchant me
agentref me # alias
```
### `merchant programs list`
List your affiliate programs.
```bash theme={null}
agentref merchant programs list
agentref merchant programs list --limit 5 --page 2
agentref merchant programs list --json
agentref programs list # alias
```
| Flag | Description |
| ----------------- | --------------------------- |
| `--limit ` | Number of programs per page |
| `--page ` | Page number |
| `--page-size ` | Items per page |
| `--offset ` | Offset-based pagination |
### `merchant programs create`
Create a new affiliate program.
```bash theme={null}
agentref merchant programs create \
--name "Acme Referrals" \
--commission-type recurring \
--commission-percent 25
agentref programs create \
--name "Acme Referrals" \
--commission-type recurring \
--commission-percent 25
```
| Flag | Required | Description |
| ------------------------------ | -------- | ----------------------------------------------- |
| `--name ` | Yes | Program name |
| `--commission-type ` | Yes | `one_time`, `recurring`, or `recurring_limited` |
| `--commission-percent <1-100>` | Yes | Commission percentage |
Additional optional flags match the `create_program` API parameters (cookie duration, payout threshold, etc.).
### `affiliate me`
Show the current affiliate workspace identity.
```bash theme={null}
agentref affiliate me
```
### `affiliate overview`
Show aggregate affiliate performance across joined programs.
```bash theme={null}
agentref affiliate overview
```
### `affiliate programs list`
List programs joined by the current affiliate account.
```bash theme={null}
agentref affiliate programs list
```
### `affiliate programs get`
Load one joined program context.
```bash theme={null}
agentref affiliate programs get --program-id PROGRAM_ID
```
### `affiliate marketplace list`
Browse the public marketplace catalog.
```bash theme={null}
agentref affiliate marketplace list
agentref affiliate marketplace list --via mcp
agentref marketplace list # alias
```
| Flag | Description |
| --------------------- | ---------------------------------------- |
| `--via ` | Transport layer to use (default: `rest`) |
| Standard filter flags | Category, min commission, etc. |
The `--via mcp` option routes the request through the MCP endpoint instead of REST, which can be useful for testing MCP connectivity.
## Exit Codes
The CLI uses structured exit codes for scripting:
| Code | Meaning |
| ---- | --------------------------------------------- |
| `0` | Success |
| `1` | Unknown / unexpected error |
| `2` | Usage error (invalid arguments) |
| `3` | Config error (missing config file or API key) |
| `4` | Auth error (401 / 403) |
| `5` | Rate limited (429) |
| `6` | Network / timeout error |
| `7` | Server error (5xx) |
| `8` | API / MCP contract or client error |
### Scripting with Exit Codes
```bash theme={null}
agentref whoami --json
if [ $? -eq 4 ]; then
echo "API key is invalid or expired"
exit 1
fi
```
## Examples
### Quick status check
```bash theme={null}
# Verify credentials work
agentref whoami
# List programs as JSON for parsing
agentref programs list --json | jq '.data[] | {name, status}'
```
### Create a program from a script
```bash theme={null}
#!/bin/bash
set -e
export AGENTREF_API_KEY="ak_live_..."
agentref programs create \
--name "Q1 Referral Program" \
--commission-type one_time \
--commission-percent 15 \
--json
echo "Program created successfully"
```
### CI/CD integration
```yaml title=".github/workflows/setup-program.yml" theme={null}
- name: Create affiliate program
env:
AGENTREF_API_KEY: ${{ secrets.AGENTREF_API_KEY }}
run: |
agentref programs create \
--name "Production Referrals" \
--commission-type recurring \
--commission-percent 20 \
--json
```
For more complex automation workflows, consider using the [Node.js SDK](/docs/agent-integration/node-sdk) or [Python SDK](/docs/agent-integration/python-sdk) instead. The CLI is best for quick operations and shell scripting.
# LLMs Context
Source: https://agentref.co/docs/agent-integration/llms-context
Use llms.txt and llms-full.txt to give AI assistants complete context about the AgentRef documentation. Feed the entire docs into Claude, ChatGPT, Cursor, or any LLM-powered tool.
AgentRef publishes machine-readable documentation endpoints that follow the [llms.txt standard](https://llmstxt.org/). These endpoints let you feed the full AgentRef documentation into any AI assistant as context -- enabling more accurate answers, code generation, and integration help.
## Endpoints
| URL | Description | Best For |
| -------------------------------------------- | ------------------------------------------------------ | ------------------------------------------ |
| `https://www.agentref.co/docs/llms.txt` | Concise overview of all documentation pages with links | Quick orientation, smaller context windows |
| `https://www.agentref.co/docs/llms-full.txt` | Complete documentation content in plain Markdown | Full context, comprehensive answers |
## What is llms.txt?
The `llms.txt` standard provides a way for websites to publish their content in a format optimized for large language models. Instead of making an LLM crawl HTML pages, you give it a single plain-text file containing everything it needs.
* **`llms.txt`** -- A structured index of all documentation pages with titles, descriptions, and URLs. Think of it as a table of contents for AI.
* **`llms-full.txt`** -- The complete documentation rendered as plain Markdown in a single file. Every page, every code example, every table -- all in one response.
## Using with AI Assistants
### Claude
Paste the URL directly in a conversation:
```
Read https://www.agentref.co/docs/llms-full.txt and then help me set up
an affiliate program with 20% recurring commission.
```
Or use Claude Projects to add it as a knowledge source:
1. Create a new Claude Project
2. Add `https://www.agentref.co/docs/llms-full.txt` as a knowledge file
3. All conversations in the project will have full AgentRef context
### ChatGPT
Use the URL in a prompt or with Browse mode:
```
Fetch https://www.agentref.co/docs/llms-full.txt and use it as context.
I need to integrate AgentRef webhooks into my Express.js app.
```
### Cursor
Add the docs to your project context in `.cursorrules` or paste the URL:
```
@https://www.agentref.co/docs/llms-full.txt
Help me create a conversion tracking integration using the AgentRef Node SDK.
```
### Other LLM Tools
Any tool that accepts URL context or plain text can use these endpoints. The content is plain Markdown with no special formatting requirements.
## Tips for Optimal Context Usage
### Choose the right endpoint
* Use **`llms.txt`** when you only need the AI to know what documentation exists and where to find it. This is lighter on tokens and works well for navigation questions.
* Use **`llms-full.txt`** when you need the AI to have deep knowledge of AgentRef's APIs, SDKs, and features. This gives the best answers but uses more context window.
### Scope your questions
Even with full documentation context, you'll get better results by being specific:
```
# Good
"Using the AgentRef Node SDK, show me how to list pending payouts
and create a payout for each affiliate that meets the threshold."
# Less good
"Help me with AgentRef."
```
### Combine with code context
For integration tasks, give the AI both the AgentRef docs and your own code:
```
I'm building a Next.js app. Here's my current webhook handler:
[your code]
Read https://www.agentref.co/docs/llms-full.txt and help me verify
the webhook signature correctly.
```
### Use llms.txt for discovery
When you are not sure which part of the docs you need, start with `llms.txt`:
```
Read https://www.agentref.co/docs/llms.txt and tell me which pages
are relevant for setting up fraud detection.
```
Then follow up with the specific pages or switch to `llms-full.txt` for the complete context.
## Programmatic Access
You can fetch these endpoints from your code for automated workflows:
```typescript Node.js theme={null}
// Fetch the full docs as context for an LLM call
const response = await fetch('https://www.agentref.co/docs/llms-full.txt');
const docsContext = await response.text();
// Use as system prompt context
const messages = [
{
role: 'system',
content: `You are an AgentRef integration assistant. Here is the full documentation:\n\n${docsContext}`
},
{
role: 'user',
content: 'How do I set up webhook signature verification?'
}
];
```
```python Python theme={null}
import httpx
# Fetch the full docs as context for an LLM call
response = httpx.get("https://www.agentref.co/docs/llms-full.txt")
docs_context = response.text
# Use as system prompt context
messages = [
{
"role": "system",
"content": f"You are an AgentRef integration assistant. Here is the full documentation:\n\n{docs_context}",
},
{
"role": "user",
"content": "How do I set up webhook signature verification?",
},
]
```
```bash cURL theme={null}
# Download for offline use
curl -o agentref-docs.md https://www.agentref.co/docs/llms-full.txt
# Quick preview
curl -s https://www.agentref.co/docs/llms.txt | head -50
```
## Content Updates
The llms.txt endpoints reflect the latest published documentation. When docs are updated, the endpoints serve the new content immediately -- no caching delay.
Bookmark `https://www.agentref.co/docs/llms-full.txt` in your AI tools. Whenever you're building an AgentRef integration, load it as context for the most accurate, up-to-date assistance.
# MCP Server
Source: https://agentref.co/docs/agent-integration/mcp-server
Connect AI agents to AgentRef through an OAuth-ready MCP server. Run merchant setup, growth, payouts, and affiliate workflows from Claude, Codex, Cursor, or OpenClaw.
AgentRef exposes [Model Context Protocol](https://modelcontextprotocol.io) servers for two personas: merchant workflows and affiliate workspace workflows. Use the merchant endpoint for program setup, growth, payouts, applications, and marketing resources. Use the affiliate endpoint for marketplace discovery, links, earnings, payout profile, and affiliate marketing resources.
Claude Code, Claude App, Codex, Cursor, and OpenClaw (`mcporter`) can all use the production MCP endpoint directly. For these clients, the recommended path is OAuth-first: add the server, approve the browser sign-in once, and let the client store refreshable credentials for future sessions.
## Endpoint
| Persona | Endpoint |
| --------- | ------------------------------------------- |
| Merchant | `https://www.agentref.co/api/mcp/merchant` |
| Affiliate | `https://www.agentref.co/api/mcp/affiliate` |
## Authentication
The MCP release contract uses snake\_case inputs and outputs. Mutation tools accept `idempotency_key` when you need retry-safe execution.
* **Preferred auth:** OAuth 2.1 with dynamic client registration
* **Supported OAuth-first clients:** Claude Code, Claude App, Codex, Cursor, OpenClaw (`mcporter`)
* **Fallback auth for custom/manual clients:** `Authorization: Bearer ak_live_*` or `ak_aff_*`
If your MCP client supports remote OAuth for Streamable HTTP servers, use that path. If it only supports static headers, create the appropriate API key in **Settings -> API Keys** and attach it manually.
## Setup
```bash theme={null}
claude mcp add --scope user --transport http agentref-merchant https://www.agentref.co/api/mcp/merchant
```
Then open `/mcp` in Claude Code, select `agentref`, and authenticate once in the browser.
1. Open `Customize -> Connectors -> + -> Add custom connector`
2. Enter `agentref` as the name
3. Enter `https://www.agentref.co/api/mcp/merchant` as the remote MCP server URL
4. Start a new conversation and send `please make a test call to the agentref mcp`
5. Confirm the tool request, then click **Reconnect** on the AgentRef connector card to authorize access
Copy this prompt into Codex:
```text theme={null}
Please run `codex mcp add agentref-merchant --url https://www.agentref.co/api/mcp/merchant` in your shell.
```
Codex runs the command for you. Allow command execution once, then complete OAuth in the browser.
If AgentRef is already configured and you only need to re-authenticate:
```bash theme={null}
codex mcp login agentref-merchant
```
In `Settings -> Tools & MCP -> Add Custom MCP`, save:
```json title=".cursor/mcp.json" theme={null}
{
"mcpServers": {
"agentref": {
"url": "https://www.agentref.co/api/mcp/merchant"
}
}
}
```
Then restart Cursor once, open the MCP settings again, and click **Connect** on the AgentRef server card.
Install or enable the `mcporter` skill in OpenClaw, then run:
```bash theme={null}
mcporter --config ~/.openclaw/workspace/config/mcporter.json config add agentref-merchant https://www.agentref.co/api/mcp/merchant --auth oauth
mcporter --config ~/.openclaw/workspace/config/mcporter.json auth --reset agentref-merchant
```
Approve the browser prompt. On success, the callback page says `Authorization successful. You can return to the CLI.`
OpenClaw also has a non-MCP fallback: `openclaw skills install agentref` plus `AGENTREF_API_KEY` in `~/.openclaw/.env`.
## Custom clients
If your MCP client supports remote OAuth, use the endpoint for the persona you need and complete the browser sign-in flow.
If your client only supports manual headers, attach an AgentRef API key:
```text theme={null}
Authorization: Bearer ak_live_your_api_key_here
```
## Workflow groups
MCP is the broadest automation surface. Some setup, payout, invite, tracking, and Marketing Resources tools call AgentRef services directly and do not have REST or SDK equivalents yet. Use the REST/SDK pages when you need only the public API v1 surface, and use MCP when an agent needs these expanded workflows.
### Merchant setup tools
| Tool | Purpose |
| ------------------------- | ------------------------------------------------------------------------- |
| `get_onboarding_status` | Read the current merchant onboarding state, blockers, and next steps. |
| `update_merchant_profile` | Update company, billing, timezone, and tracking defaults. |
| `create_program` | Create a new affiliate program. |
| `list_programs` | List programs or read one exact program by `program_id`. |
| `update_program` | Update one program's settings, branding, marketplace state, or lifecycle. |
| `get_stripe_connect_url` | Generate the Stripe Connect authorization URL for one program. |
| `complete_onboarding` | Finalize merchant onboarding when the setup anchors are ready. |
| `get_tracking_snippet` | Return the JavaScript snippet and install instructions for one program. |
| `verify_tracking` | Check tracking health and return warnings plus next steps. |
### Merchant growth tools
| Tool | Purpose |
| ---------------------- | -------------------------------------------------------------------------------- |
| `list_affiliates` | List affiliates for one merchant, or read one exact affiliate by `affiliate_id`. |
| `list_applications` | List marketplace and portal applications, or read one exact application. |
| `review_application` | Approve, decline, or block one pending application. |
| `set_affiliate_status` | Block or unblock an approved affiliate membership. |
| `list_invites` | List invite links for one program. |
| `create_invite` | Create a public or targeted invite link. |
| `revoke_invite` | Revoke one invite so it cannot be claimed again. |
| `get_program_overview` | Load key KPIs, status counts, and warnings for one program. |
| `list_conversions` | List merchant conversions with filters. |
| `list_flags` | List fraud and review flags, or load one exact flag. |
| `review_flag` | Resolve one fraud flag as reviewed, dismissed, or confirmed. |
### Merchant payout tools
| Tool | Purpose |
| ----------------------- | ------------------------------------------------------------------------ |
| `list_pending_payouts` | List payout-ready affiliates who already meet the threshold. |
| `list_payouts` | List manual payout records with filters. |
| `list_upcoming_payouts` | List affiliates with released unpaid commissions plus next payout date. |
| `create_payout` | Create one manual payout record for an affiliate-program pair. |
| `update_payout_status` | Advance a payout to `processing`, `completed`, or `failed`. |
| `export_payouts_csv` | Export open manual payouts for PayPal, Wise, SEPA, or generic workflows. |
### Merchant marketing resources tools
| Tool | Purpose |
| --------------------------------------------------- | ------------------------------------------------------------------------------ |
| `list_marketing_resources` | List merchant-owned collections, files, social posts, links, and draft assets. |
| `get_marketing_resource` | Read one marketing resource or collection. |
| `create_marketing_collection` | Create a campaign collection. |
| `update_marketing_collection` | Update collection metadata. |
| `publish_marketing_resource` | Publish a resource or collection for affiliates. |
| `unpublish_marketing_resource` | Move a resource or collection back to draft. |
| `archive_marketing_resource` | Archive a resource or collection. |
| `create_marketing_social_post` | Create affiliate-ready social post copy. |
| `update_marketing_social_post` | Update social post copy, platforms, instructions, or grouping. |
| `create_marketing_social_post_media_upload_session` | Create a signed upload session for social post media. |
| `complete_marketing_social_post_media_upload` | Attach uploaded media to a social post. |
| `remove_marketing_social_post_media` | Remove social post media. |
| `update_marketing_social_post_media` | Update media alt text. |
| `reorder_marketing_social_post_media` | Normalize social post media order. |
| `replace_marketing_social_post_media` | Replace a media file while preserving its media record. |
| `create_marketing_resource_download_url` | Create a short-lived merchant download URL. |
| `create_marketing_external_link` | Add an external resource link. |
| `create_marketing_upload_session` | Create a signed upload session for a file or ZIP import. |
| `complete_marketing_upload_session` | Complete an uploaded file or ZIP import. |
| `import_marketing_resource_from_url` | Import a remote file or ZIP into private storage. |
| `notify_marketing_resource_affiliates` | Queue affiliate notifications for a published resource. |
### Affiliate workspace tools
| Tool | Purpose |
| -------------------------- | ------------------------------------------------------------------------------- |
| `discover_programs` | Search public marketplace programs with filters. |
| `apply_to_program` | Apply the current affiliate account to a marketplace program. |
| `get_affiliate_overview` | Return aggregate affiliate workspace KPIs across programs. |
| `list_my_programs` | List the current affiliate account and linked program context. |
| `list_links` | List existing tracking links for the current affiliate account. |
| `create_link` | Create one affiliate tracking link for the current program. |
| `get_earnings` | Return a compact earnings summary for the current affiliate account. |
| `get_click_stats` | Return daily click and unique click totals for the current affiliate account. |
| `list_my_payouts` | List payout history for the current affiliate account. |
| `update_my_payout_profile` | Update payout destination and billing fields for the current affiliate account. |
### Affiliate marketing resources tools
| Tool | Purpose |
| ---------------------------------------- | ---------------------------------------------------------------------------- |
| `list_marketing_resources` | List affiliate-visible resources for a program. |
| `get_marketing_resource` | Read one affiliate-visible resource or collection. |
| `render_marketing_social_post` | Render social post copy with affiliate placeholders and disclosure guidance. |
| `create_marketing_resource_download_url` | Create a short-lived affiliate download URL. |
## Resources
The servers also expose read-oriented resources for structured data access:
| Persona | Resource URI | Description |
| --------- | ------------------------------------------------- | ------------------------------------------------------------------ |
| Merchant | `merchant://me` | Authenticated merchant profile and baseline account configuration. |
| Merchant | `program://{id}` | Program configuration and ownership metadata. |
| Merchant | `stats://{programId}` | Program-level conversion and commission statistics. |
| Merchant | `marketing-resources://program/{program_id}` | Marketing resources for a program. |
| Merchant | `marketing-resource-collection://{collection_id}` | One marketing resource collection. |
| Merchant | `marketing-resource://{resource_id}` | One marketing resource item. |
| Affiliate | `affiliate://me/programs` | Authenticated affiliate program context and performance summary. |
| Affiliate | `marketplace://catalog` | Catalog of public marketplace programs. |
| Affiliate | `marketing-resources://program/{program_id}` | Affiliate-visible marketing resources for a program. |
| Affiliate | `marketing-resource-collection://{collection_id}` | Affiliate-visible collection details. |
| Affiliate | `marketing-resource://{resource_id}` | Affiliate-visible resource details. |
## Prompts
The MCP server also registers four reusable prompts for guided workflows:
| Prompt | Purpose |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `onboard_program` | Guide an agent through program creation, Stripe connect, tracking verification, and onboarding completion. |
| `review_payout_run` | Guide a manual payout run from candidate discovery to final payout status updates. |
| `debug_tracking_installation` | Diagnose an incomplete or broken tracking installation for one program. |
| `triage_fraud_flags` | Review open fraud flags, prioritize them, and resolve them safely. |
## Idempotency
Mutation tools accept an optional `idempotency_key`. When provided, the server guarantees at-most-once execution for the logical operation -- safe for agent retries.
```
idempotency_key: "setup-acme-program-2026-04"
```
Always pass an idempotency key when your agent might retry a tool call. This prevents duplicate programs, double payouts, or repeated approvals.
## Workflow Examples
### Set Up a New Program
A natural conversation with your AI agent might look like:
> **You:** "Create a SaaS affiliate program called 'Acme Pro Referrals' with 25% recurring commission and a 60-day cookie."
The agent calls:
```
get_onboarding_status({})
create_program({
name: "Acme Pro Referrals",
commission_percent: 25,
commission_type: "recurring",
cookie_duration: 60,
idempotency_key: "create-acme-pro-referrals"
})
```
Then it follows up with `get_stripe_connect_url`, `get_tracking_snippet`, `verify_tracking`, and `complete_onboarding`.
### Monitor Fraud Flags
> **You:** "Check if there are any suspicious conversions this week and resolve them."
The agent can start with `list_flags`, then resolve specific items with `review_flag`:
```
list_flags({
status: "open",
limit: 20
})
review_flag({
flag_id: "flag-uuid-here",
resolution: "dismissed",
note: "Verified legitimate traffic from blog post",
idempotency_key: "resolve-flag-abc123"
})
```
### Generate a Payout Report
> **You:** "Show me who's ready for payout, create the payout records, and export a CSV for PayPal."
```
list_pending_payouts({ program_id: "prog-uuid" })
create_payout({
affiliate_id: "aff-uuid",
program_id: "prog-uuid",
idempotency_key: "payout-acme-april"
})
export_payouts_csv({ format: "paypal", program_id: "prog-uuid" })
```
### Affiliate: Discover and Join Programs
> **You:** "Find affiliate programs with at least 20% commission and apply to the best one."
```
discover_programs({ min_commission: 20, sort_by: "commission" })
apply_to_program({
program_id: "prog-uuid",
message: "I run a SaaS review blog with 50k monthly readers.",
idempotency_key: "apply-prog-xyz"
})
create_link({
program_id: "prog-uuid",
destination_path: "/pricing",
custom_slug: "acme-review",
idempotency_key: "link-acme-pricing"
})
```
### Merchant: Publish Campaign Assets
> **You:** "Create a LinkedIn post for our launch kit and publish it for affiliates."
```
create_marketing_social_post({
program_id: "prog-uuid",
title: "Launch announcement",
body: "I am sharing {{program_name}} because it helps teams launch affiliate programs faster: {{affiliate_link}}",
platforms: ["linkedin"],
status: "draft",
idempotency_key: "launch-post-001"
})
publish_marketing_resource({
program_id: "prog-uuid",
resource_id: "resource-uuid",
idempotency_key: "publish-launch-post-001"
})
```
### Affiliate: Render Campaign Copy
> **You:** "Show me the latest social posts for this program and render one with my link."
```
list_marketing_resources({
program_id: "prog-uuid",
kind: "social_posts"
})
render_marketing_social_post({
program_id: "prog-uuid",
resource_id: "resource-uuid",
affiliate_link_id: "link-uuid"
})
```
## Sessions and transport
* The endpoints use Streamable HTTP at `https://www.agentref.co/api/mcp/merchant` and `https://www.agentref.co/api/mcp/affiliate`
* Sessions are created during the MCP `initialize` flow and handled automatically by clients
* If a client holds an expired or invalid session id, it should reconnect and initialize again
## Error Handling
Tool errors are returned as structured MCP results with an error code, message, and HTTP status. Common scenarios:
| Error | Cause |
| ------------------ | ---------------------------------------------------- |
| `UNAUTHORIZED` | OAuth not completed yet, or invalid fallback API key |
| `FORBIDDEN` | Key lacks required scope |
| `NOT_FOUND` | Program, affiliate, or flag does not exist |
| `VALIDATION_ERROR` | Invalid input parameters |
| `RATE_LIMITED` | Too many requests -- retry after delay |
All errors include a `requestId` for debugging. Reference it when contacting support.
# Node.js SDK
Source: https://agentref.co/docs/agent-integration/node-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) |
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 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',
});
```
`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.
## 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`:
```typescript theme={null}
interface PaginatedResponse {
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';
```
# Python SDK
Source: https://agentref.co/docs/agent-integration/python-sdk
Official Python SDK for the AgentRef REST API. Manage programs, affiliates, conversions, payouts, fraud flags, billing, webhooks, and merchant settings with sync and async clients.
The `agentref` Python package provides both synchronous and asynchronous clients for the AgentRef REST API v1. It uses [httpx](https://www.python-httpx.org/) under the hood and returns [Pydantic](https://docs.pydantic.dev/) models for core resources. Some flexible surfaces, including Marketing Resources and parts of the affiliate workspace, return dictionaries or lists of dictionaries because their response shape can vary by resource kind.
## Installation
```bash theme={null}
pip install agentref
```
## Quick Start
```python theme={null}
from agentref import AgentRef
client = AgentRef(api_key="ak_live_your_api_key_here")
# List all programs
result = client.programs.list()
print(f"Found {result.meta.total} programs")
# Get conversion stats
stats = client.conversions.stats(period="30d")
print(f"Revenue: ${stats.total_revenue / 100}")
```
### Async Usage
```python theme={null}
from agentref import AsyncAgentRef
async with AsyncAgentRef(api_key="ak_live_...") as client:
result = await client.programs.list()
for program in result.data:
print(program.name)
```
## Configuration
```python theme={null}
client = AgentRef(
api_key="ak_live_...", # or set AGENTREF_API_KEY env var
base_url="https://www.agentref.co/api/v1", # default
timeout=30.0, # 30s default
max_retries=2, # retries for GET + idempotent POST
)
```
| Parameter | Type | Default | Description |
| ------------- | ------- | -------------------------------- | ---------------------------------------- |
| `api_key` | `str` | `AGENTREF_API_KEY` env | Your API key. Required. |
| `base_url` | `str` | `https://www.agentref.co/api/v1` | API base URL |
| `timeout` | `float` | `30.0` | Request timeout in seconds |
| `max_retries` | `int` | `2` | Max retries for safe/idempotent requests |
## 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.affiliate_workspace` | Affiliate self-serve overview, programs, links, earnings, payouts, and clicks |
| `client.marketing_resources` | 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.payout_info` | Get and update payout payment 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
```python theme={null}
# List programs (paginated)
result = client.programs.list(status="active", limit=10, page=1)
# Auto-paginate through all programs
for program in client.programs.list_all():
print(program.name)
# Async auto-pagination
async for program in client.programs.list_all():
print(program.name)
# Get program details (includes readiness status)
detail = client.programs.get("prog-uuid")
print(detail.readiness) # 'setup' | 'partial' | 'ready'
# Create a program
program = client.programs.create(
name="Acme Referrals",
commission_type="recurring",
commission_percent=25,
cookie_duration=60,
currency="USD",
idempotency_key="create-acme-v1",
)
# Update a program
client.programs.update("prog-uuid", commission_percent=30, status="active")
# Delete a program
client.programs.delete("prog-uuid")
# Get program stats
stats = client.programs.stats("prog-uuid", period="30d")
# List program affiliates
affiliates = client.programs.list_affiliates("prog-uuid", include_blocked=False)
# Manage invites
invite = client.programs.create_invite(
"prog-uuid",
email="partner@example.com",
expires_in_days=7,
idempotency_key="invite-partner-v1",
)
invites = client.programs.list_invites("prog-uuid")
# Manage coupons
coupon = client.programs.create_coupon(
"prog-uuid",
affiliate_id="aff-uuid",
code="PARTNER20",
idempotency_key="coupon-partner20",
)
coupons = client.programs.list_coupons("prog-uuid")
client.programs.delete_coupon("coupon-uuid")
# Marketplace settings
client.programs.update_marketplace(
"prog-uuid",
status="public",
category="SaaS",
description="Earn 25% recurring for every referral.",
)
# Stripe connection
stripe = client.programs.connect_stripe("prog-uuid")
print(stripe.auth_url) # redirect merchant to complete OAuth
client.programs.disconnect_stripe("prog-uuid")
```
## Applications
```python theme={null}
# Pending joins live in the Applications lifecycle
pending = client.applications.list(
program_id="prog-uuid",
status="pending",
)
client.applications.approve(
pending.data[0].id,
note="Relevant SaaS audience.",
idempotency_key="approve-app-123",
)
client.applications.decline(
"app-uuid",
note="Audience is not a fit.",
)
```
## Affiliates
```python theme={null}
# List approved or blocked affiliate memberships
result = client.affiliates.list(
program_id="prog-uuid",
status="approved",
search="john",
sort_by="totalRevenue",
sort_order="desc",
limit=25,
)
# Get single affiliate (with optional stats)
affiliate = client.affiliates.get("aff-uuid", include="stats")
# Block with reason
client.affiliates.block(
"aff-uuid",
reason="Suspicious click patterns detected",
idempotency_key="block-aff-123",
)
# Unblock
client.affiliates.unblock("aff-uuid", idempotency_key="unblock-aff-123")
```
## Affiliate Workspace
```python theme={null}
identity = client.affiliate_workspace.identity()
overview = client.affiliate_workspace.overview()
programs = client.affiliate_workspace.list_programs()
link = client.affiliate_workspace.create_link(
name="Pricing",
destination_path="/pricing",
custom_slug="jane-review",
program_id=programs[0].program_id,
idempotency_key="create-pricing-link",
)
links = client.affiliate_workspace.list_links(program_id=programs[0].program_id)
client.affiliate_workspace.update_link(
link["id"],
name="Pricing - updated",
target_url="https://merchant-site.com/pricing",
is_active=True,
program_id=programs[0].program_id,
)
client.affiliate_workspace.delete_link(
"old-link-uuid",
program_id=programs[0].program_id,
)
earnings = client.affiliate_workspace.earnings()
program_earnings = client.affiliate_workspace.list_program_earnings(
programs[0].program_id,
period="30d",
)
payouts = client.affiliate_workspace.list_payouts(
program_id=programs[0].program_id,
)
clicks = client.affiliate_workspace.click_stats(
program_id=programs[0].program_id,
start_date="2026-05-01",
)
```
`create_link` uses `destination_path` and `custom_slug`. The current update API accepts `name`, `target_url`, and `is_active`; create a new link when you need a different destination path or custom slug.
## Marketing Resources
```python theme={null}
# Merchant workflow
post = client.marketing_resources.create_social_post(
"prog-uuid",
title="Launch copy",
body="Try AgentRef: {{affiliate_link}}",
platforms=["linkedin"],
status="published",
idempotency_key="mr-launch-copy",
)
client.marketing_resources.publish(
post["id"],
program_id="prog-uuid",
notify_affiliates=True,
idempotency_key="publish-mr-launch-copy",
)
client.marketing_resources.update_social_post(
post["id"],
program_id="prog-uuid",
title="Launch copy v2",
)
upload = client.marketing_resources.create_social_post_media_upload_session(
post["id"],
program_id="prog-uuid",
file_name="launch.png",
mime_type="image/png",
file_size_bytes=125_000,
idempotency_key="launch-media-upload",
)
# Upload the file bytes to upload["signed_url"], then attach the uploaded media.
client.marketing_resources.complete_social_post_media_upload(
post["id"],
program_id="prog-uuid",
upload_path=upload["upload_path"],
upload_token=upload["upload_token"],
file_name="launch.png",
alt_text="Launch product screenshot",
idempotency_key="launch-media-complete",
)
client.marketing_resources.create_download_url(
program_id="prog-uuid",
resource_id=post["id"],
idempotency_key="download-launch-copy",
)
# Affiliate workflow
resources = client.marketing_resources.list_for_affiliate(
"prog-uuid",
kind="social_posts",
)
resource = client.marketing_resources.get_for_affiliate(
resources[0]["id"],
program_id="prog-uuid",
)
rendered = client.marketing_resources.render_social_post(
resources[0]["id"],
program_id="prog-uuid",
affiliate_link_id=link["id"],
)
download = client.marketing_resources.create_affiliate_download_url(
program_id="prog-uuid",
resource_id=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`, `create_social_post`, `update_social_post` | Merchant REST/SDK |
| `create_social_post_media_upload_session`, `complete_social_post_media_upload`, `replace_social_post_media` | Merchant REST/SDK |
| `reorder_social_post_media`, `update_social_post_media`, `remove_social_post_media` | Merchant REST/SDK |
| `publish`, `unpublish`, `archive`, `notify_affiliates`, `create_download_url` | Merchant REST/SDK |
| `list_for_affiliate`, `get_for_affiliate`, `render_social_post`, `create_affiliate_download_url` | Affiliate REST/SDK |
| Collections, external links, generic file upload, URL import | Merchant MCP only |
## Conversions
```python theme={null}
# List conversions with filters
result = client.conversions.list(
program_id="prog-uuid",
status="pending",
start_date="2026-01-01",
end_date="2026-03-23",
limit=50,
)
# Aggregate stats
stats = client.conversions.stats(program_id="prog-uuid", period="30d")
print(f"{stats.total} conversions, ${stats.total_revenue / 100} revenue")
# Recent conversions
recent = client.conversions.recent(limit=5)
```
## Payouts
```python theme={null}
# List completed payouts
result = client.payouts.list(status="completed", start_date="2026-01-01")
# List pending affiliates (ready for payout)
pending = client.payouts.list_pending(program_id="prog-uuid")
for aff in pending.data:
print(f"{aff.name}: ${aff.pending_amount / 100} ({aff.currency})")
# Payout stats
payout_stats = client.payouts.stats(period="30d")
# Create a payout
payout = client.payouts.create(
affiliate_id="aff-uuid",
program_id="prog-uuid",
method="paypal",
notes="March payout",
idempotency_key="payout-march-aff123",
)
```
## Fraud Flags
```python theme={null}
# List open flags
result = client.flags.list(status="open", limit=20)
# Get flag stats
flag_stats = client.flags.stats()
print(f"{flag_stats.open} open flags")
# Resolve a flag
client.flags.resolve(
"flag-uuid",
status="dismissed",
note="Verified legitimate traffic",
block_affiliate=False,
idempotency_key="resolve-flag-abc",
)
```
## Billing
```python theme={null}
# Check current billing status
billing = client.billing.current()
print(f"Tier: {billing.tier}, Revenue: ${billing.monthly_revenue / 100}")
# List available tiers
tiers = client.billing.tiers()
# Subscribe to a tier
client.billing.subscribe(tier="growth", idempotency_key="subscribe-growth-v1")
```
## Merchant
```python theme={null}
# Get merchant profile
merchant = client.merchant.get()
# Update merchant settings
client.merchant.update(
company_name="Acme Inc.",
timezone="America/New_York",
default_cookie_duration=60,
)
```
## Notifications
```python theme={null}
# Get notification preferences
prefs = client.notifications.get()
# Update preferences
client.notifications.update(
new_affiliate=True,
new_conversion=True,
weekly_digest=True,
)
```
## Payout Info
```python theme={null}
# Get payout info
info = client.payout_info.get()
# Update payout info
client.payout_info.update(
payout_method="paypal",
paypal_email="payments@acme.com",
)
```
## Onboarding, Tracking, Invites, and Marketplace
```python theme={null}
client.onboarding.upsert_merchant_profile(company_name="Acme Inc.")
client.onboarding.complete()
tracking = client.tracking.get_program_status("prog-uuid")
invite = client.invites.get("invite-token")
claimed = client.invites.claim(invite["token"])
marketplace = client.marketplace.list_programs(
sort="commission",
min_commission=20,
)
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 `company_name` and onboarding completion. Merchant MCP has additional setup tools for `get_onboarding_status`, `get_tracking_snippet`, and active tracking verification.
## Webhooks
```python theme={null}
# List webhook endpoints
endpoints = client.webhooks.list(program_id="prog-uuid")
# Create endpoint
result = client.webhooks.create(
name="Production Webhook",
url="https://api.acme.com/webhooks/agentref",
subscribed_events=[
"conversion.created",
"payout.completed",
"affiliate.joined",
],
program_id="prog-uuid",
)
signing_secret = result.signing_secret # store securely -- shown only once
# Update endpoint
client.webhooks.update(
"wh-uuid",
subscribed_events=[
"conversion.created",
"conversion.refunded",
"payout.completed",
],
)
# Rotate signing secret
new_result = client.webhooks.rotate_secret("wh-uuid")
# Delete endpoint
client.webhooks.delete("wh-uuid")
```
***
## Pagination
Paginated REST list methods return a `PaginatedResponse[T]` with typed data and metadata:
```python theme={null}
result = client.programs.list(page=1, limit=25)
print(result.data) # List[Program]
print(result.meta.total) # Total count
print(result.meta.has_more) # More pages available?
print(result.meta.page) # Current page
```
Some convenience list methods return plain lists because their REST endpoints return array data directly. Examples include `client.affiliate_workspace.list_programs()`, `client.affiliate_workspace.list_links()`, `client.marketing_resources.list()`, `client.marketing_resources.list_for_affiliate()`, `client.webhooks.list()`, and `client.conversions.recent()`.
### Auto-pagination
The `list_all()` generator handles pagination automatically:
```python theme={null}
# Sync
for program in client.programs.list_all(page_size=100):
print(program.name)
# Async
async for program in client.programs.list_all(page_size=100):
print(program.name)
```
## Idempotency
POST mutation methods accept an `idempotency_key` keyword argument. When provided, the request is safe to retry -- the server guarantees at-most-once execution.
```python theme={null}
program = client.programs.create(
name="Acme Referrals",
commission_type="recurring",
commission_percent=25,
idempotency_key="create-acme-referrals-v1",
)
```
The SDK automatically retries failed requests (up to `max_retries`) 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 raises typed exceptions for all API failures:
```python theme={null}
from agentref import (
AgentRefError,
AuthError,
ForbiddenError,
ValidationError,
NotFoundError,
ConflictError,
RateLimitError,
ServerError,
)
try:
client.programs.get("nonexistent-id")
except NotFoundError as e:
print(f"Not found: {e}")
print(f"Request ID: {e.request_id}")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after}s")
except ValidationError as e:
print(f"Validation failed: {e}")
print(f"Details: {e.details}")
except AgentRefError as e:
print(f"API error {e.status}: {e.code} - {e}")
```
| Exception 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 exceptions inherit from `AgentRefError` and include `code`, `status`, and `request_id` attributes.
## Sync vs Async
The Python SDK ships with both synchronous and asynchronous clients:
```python Sync theme={null}
from agentref import AgentRef
client = AgentRef(api_key="ak_live_...")
programs = client.programs.list()
client.close() # optional cleanup
```
```python Async theme={null}
from agentref import AsyncAgentRef
async with AsyncAgentRef(api_key="ak_live_...") as client:
programs = await client.programs.list()
# connection pooling managed automatically
```
The async client supports `async with` for automatic connection cleanup. All async resource methods are identical to their sync counterparts but return coroutines.
# Block affiliate
Source: https://agentref.co/docs/api-reference/affiliates/block-affiliate
/openapi.json post /affiliates/{id}/block
# Get affiliate by id
Source: https://agentref.co/docs/api-reference/affiliates/get-affiliate-by-id
/openapi.json get /affiliates/{id}
# List affiliates
Source: https://agentref.co/docs/api-reference/affiliates/list-affiliates
/openapi.json get /affiliates
# Unblock affiliate
Source: https://agentref.co/docs/api-reference/affiliates/unblock-affiliate
/openapi.json post /affiliates/{id}/unblock
# Approve application and create affiliate membership
Source: https://agentref.co/docs/api-reference/applications/approve-application-and-create-affiliate-membership
/openapi.json post /applications/{id}/approve
# Block an application
Source: https://agentref.co/docs/api-reference/applications/block-an-application
/openapi.json post /applications/{id}/block
# Decline an application
Source: https://agentref.co/docs/api-reference/applications/decline-an-application
/openapi.json post /applications/{id}/decline
# Get application by id
Source: https://agentref.co/docs/api-reference/applications/get-application-by-id
/openapi.json get /applications/{id}
# List affiliate applications
Source: https://agentref.co/docs/api-reference/applications/list-affiliate-applications
/openapi.json get /applications
# Authentication
Source: https://agentref.co/docs/api-reference/authentication
How to authenticate with the AgentRef API using API keys.
All API requests require authentication via a Bearer token. Your API key acts as the token.
## API Key Format
AgentRef uses prefixed API keys to distinguish key types:
| Prefix | Type | Description |
| ---------- | --------- | ---------------------------------------------------------------------------- |
| `ak_live_` | Merchant | Full access to your programs, affiliates, conversions, payouts, and settings |
| `ak_aff_` | Affiliate | Access to affiliate self-serve endpoints (`/me/*`, marketplace) |
## Making Requests
Include your API key as a Bearer token in the `Authorization` header:
```bash cURL theme={null}
curl https://www.agentref.co/api/v1/programs \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
```javascript Node.js theme={null}
import { AgentRef } from 'agentref'
const client = new AgentRef({ apiKey: 'ak_live_YOUR_KEY' })
const programs = await client.programs.list()
```
```python Python theme={null}
from agentref import AgentRef
client = AgentRef(api_key="ak_live_YOUR_KEY")
programs = client.programs.list()
```
## Base URL
All API endpoints are served from:
```
https://www.agentref.co/api/v1
```
## Scopes
API keys are scoped to control access. When creating a key, you choose which scopes to grant.
### Merchant Scopes
| Scope | Description |
| --------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `merchant:read` | Read merchant profile and settings |
| `merchant:manage` | Update merchant profile and settings |
| `programs:read` | List and view programs |
| `programs:write` | Create and update programs |
| `affiliates:read` | List and view affiliates |
| `affiliates:manage` | Review applications and block or unblock approved affiliates |
| `conversions:read` | List and view conversions |
| `payouts:read` | List and view payouts |
| `payouts:write` | Create payouts and mark as completed |
| `stats:read` | View program, conversion, and payout statistics |
| `billing:read` | View billing information and tiers |
| `billing:write` | Subscribe and manage billing |
| `flags:read` | List and view fraud flags |
| `flags:write` | Resolve fraud flags |
| `webhooks:read` | List and view webhook endpoints |
| `webhooks:write` | Create, update, and delete webhook endpoints |
| `marketing_resources:read` | List, view, and download marketing resources |
| `marketing_resources:write` | Create social posts, manage media/status, create download URLs, and notify affiliates about marketing resources |
| `invites:claim` | Claim invite tokens |
### Affiliate Scopes
| Scope | Description |
| -------------------------- | ------------------------------------------------------------------ |
| `profile:read` | View own profile |
| `profile:write` | Update own profile |
| `links:read` | List own tracking links |
| `links:write` | Create, update, and delete tracking links |
| `earnings:read` | View own commission earnings |
| `conversions:read` | View own conversions |
| `payouts:read` | View own payout history |
| `stats:read` | View own statistics |
| `marketplace:read` | Browse the program marketplace |
| `marketplace:apply` | Apply to programs |
| `marketing_resources:read` | View, render, and download marketing resources for joined programs |
| `invites:claim` | Claim invite tokens |
### Scope Presets
For convenience, you can use presets when creating API keys:
| Preset | Includes |
| ---------------- | ------------------------------------------------------- |
| `merchant:full` | All merchant scopes |
| `affiliate:full` | All affiliate scopes |
| `readonly` | All read-only scopes across both merchant and affiliate |
## Creating API Keys
1. Log in to your [AgentRef dashboard](https://www.agentref.co)
2. Go to **Settings → API Keys**
3. Click **Create API Key**
4. Select scopes or a preset
5. Copy the key – it won't be shown again
Store your API key securely. Treat it like a password. Never commit API keys to version control or expose them in client-side code.
## Authentication Errors
| Status | Code | Description |
| ------ | -------------------- | ----------------------------------------------------------------- |
| `401` | `UNAUTHORIZED` | Missing or invalid API key |
| `403` | `INSUFFICIENT_SCOPE` | API key doesn't have the required scope for this action |
| `403` | `FORBIDDEN` | Action not allowed (e.g., accessing another merchant's resources) |
# Get billing overview
Source: https://agentref.co/docs/api-reference/billing/get-billing-overview
/openapi.json get /billing
# List available billing tiers
Source: https://agentref.co/docs/api-reference/billing/list-available-billing-tiers
/openapi.json get /billing/tiers
# Start billing subscription checkout
Source: https://agentref.co/docs/api-reference/billing/start-billing-subscription-checkout
/openapi.json post /billing/subscribe
# Get conversion stats
Source: https://agentref.co/docs/api-reference/conversions/get-conversion-stats
/openapi.json get /conversions/stats
# Get recent conversions
Source: https://agentref.co/docs/api-reference/conversions/get-recent-conversions
/openapi.json get /conversions/recent
# List conversions
Source: https://agentref.co/docs/api-reference/conversions/list-conversions
/openapi.json get /conversions
# Delete coupon
Source: https://agentref.co/docs/api-reference/coupons/delete-coupon
/openapi.json delete /coupons/{id}
# Hosted API documentation UI
Source: https://agentref.co/docs/api-reference/docs/hosted-api-documentation-ui
/openapi.json get /docs
# Errors
Source: https://agentref.co/docs/api-reference/errors
Error response format, HTTP status codes, and rate limiting.
The AgentRef API uses standard HTTP status codes and returns structured error responses.
## Error Response Format
All errors follow the same structure:
```json theme={null}
{
"error": {
"code": "BAD_REQUEST",
"message": "Invalid request. Please check your input",
"details": {}
},
"meta": {
"requestId": "req_abc123"
}
}
```
| Field | Type | Description |
| ---------------- | -------- | -------------------------------------------- |
| `error.code` | `string` | Machine-readable error code |
| `error.message` | `string` | Human-readable description |
| `error.details` | `object` | Additional context (e.g., validation errors) |
| `meta.requestId` | `string` | Unique request ID for debugging |
## HTTP Status Codes
| Status | Code | Description |
| ------ | ------------------------ | ----------------------------------------- |
| `400` | `BAD_REQUEST` | Invalid request parameters or body |
| `401` | `UNAUTHORIZED` | Missing or invalid API key |
| `403` | `FORBIDDEN` | Not allowed to access this resource |
| `403` | `INSUFFICIENT_SCOPE` | API key missing required scope |
| `404` | `NOT_FOUND` | Resource not found |
| `409` | `CONFLICT` | Operation conflicts with existing data |
| `409` | `IDEMPOTENCY_KEY_REUSED` | Idempotency key used with different input |
| `422` | `VALIDATION_ERROR` | Request body fails validation |
| `429` | `RATE_LIMITED` | Too many requests |
| `500` | `INTERNAL_ERROR` | Something went wrong on our side |
## Error Handling Example
```javascript Node.js theme={null}
import {
AgentRef,
ForbiddenError,
NotFoundError,
RateLimitError,
} from 'agentref'
const client = new AgentRef({ apiKey: 'ak_live_YOUR_KEY' })
try {
const program = await client.programs.get('nonexistent')
} catch (error) {
if (error instanceof NotFoundError) {
console.log('Program not found')
} else if (error instanceof RateLimitError) {
console.log('Rate limited, retry after:', error.retryAfter)
} else if (error instanceof ForbiddenError) {
console.log('Insufficient permissions')
} else {
console.error('Unexpected error:', error.message)
}
}
```
```python Python theme={null}
from agentref import AgentRef
from agentref.errors import NotFoundError, RateLimitError, ForbiddenError
client = AgentRef(api_key="ak_live_YOUR_KEY")
try:
program = client.programs.get("nonexistent")
except NotFoundError:
print("Program not found")
except RateLimitError as e:
print(f"Rate limited, retry after: {e.retry_after}")
except ForbiddenError:
print("Insufficient permissions")
except Exception as e:
print(f"Unexpected error: {e}")
```
## Rate Limiting
API requests are rate limited per API key using a sliding window.
### Rate Limits by Tier
| Tier | Requests | Window |
| --------------- | -------- | -------- |
| Unauthenticated | 10 | 1 minute |
| Free | 60 | 1 minute |
| Starter | 60 | 1 minute |
### Rate Limit Headers
Every response includes rate limit headers:
| Header | Description |
| ----------------------- | ----------------------------------------- |
| `X-RateLimit-Limit` | Maximum requests allowed in the window |
| `X-RateLimit-Remaining` | Requests remaining in the current window |
| `X-RateLimit-Reset` | Unix timestamp when the window resets |
| `Retry-After` | Seconds to wait (only on `429` responses) |
### Handling Rate Limits
```javascript Node.js theme={null}
// The AgentRef SDK handles retries automatically
const client = new AgentRef({
apiKey: 'ak_live_YOUR_KEY',
maxRetries: 3
})
```
```python Python theme={null}
# The AgentRef SDK handles retries automatically
client = AgentRef(
api_key="ak_live_YOUR_KEY",
max_retries=3
)
```
```bash cURL theme={null}
# Check the Retry-After header on 429 responses
curl -s -D - -H "Authorization: Bearer ak_live_YOUR_KEY" \
https://www.agentref.co/api/v1/programs
# HTTP/2 429
# Retry-After: 12
# X-RateLimit-Remaining: 0
```
# Get fraud flag stats
Source: https://agentref.co/docs/api-reference/flags/get-fraud-flag-stats
/openapi.json get /flags/stats
# List fraud flags
Source: https://agentref.co/docs/api-reference/flags/list-fraud-flags
/openapi.json get /flags
# Resolve fraud flag
Source: https://agentref.co/docs/api-reference/flags/resolve-fraud-flag
/openapi.json post /flags/{id}/resolve
# Idempotency
Source: https://agentref.co/docs/api-reference/idempotency
How to safely retry API requests without creating duplicate resources.
Idempotency lets you safely retry requests without side effects. If a network error occurs and you're unsure whether your request succeeded, you can retry with the same idempotency key.
## How It Works
Include an `Idempotency-Key` header with POST requests that create or trigger work. The key lets the server return the original result if your client retries the same request after a timeout or network failure.
```bash cURL theme={null}
curl -X POST https://www.agentref.co/api/v1/programs \
-H "Authorization: Bearer ak_live_YOUR_KEY" \
-H "Idempotency-Key: create-acme-referrals-v1" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Referrals",
"commissionType": "recurring",
"commissionPercent": 25,
"currency": "USD"
}'
```
```javascript Node.js theme={null}
const program = await client.programs.create({
name: 'Acme Referrals',
commissionType: 'recurring',
commissionPercent: 25,
currency: 'USD',
}, {
idempotencyKey: 'create-acme-referrals-v1',
})
```
```python Python theme={null}
program = client.programs.create(
name="Acme Referrals",
commission_type="recurring",
commission_percent=25,
currency="USD",
idempotency_key="create-acme-referrals-v1",
)
```
## Behavior
| Scenario | Result |
| ------------------------------------ | ------------------------------------ |
| First request with a key | Processed normally, result cached |
| Retry with same key + same body | Returns cached result (no duplicate) |
| Retry with same key + different body | `409 IDEMPOTENCY_KEY_REUSED` error |
| Key expires (after 24 hours) | Treated as a new request |
## Generating Keys
Use a UUID v4 or combine business-meaningful values:
```javascript Node.js theme={null}
import { randomUUID } from 'crypto'
// Option 1: Random UUID
const key = randomUUID()
// Option 2: Business-meaningful (prevents duplicates across retries AND code paths)
const key = `payout_${affiliateId}_${period}`
```
```python Python theme={null}
import uuid
# Option 1: Random UUID
key = str(uuid.uuid4())
# Option 2: Business-meaningful
key = f"payout_{affiliate_id}_{period}"
```
## Best Practices
Idempotency keys are only meaningful for mutation requests that accept them. The Node.js and Python SDKs attach idempotency keys to POST requests when you pass the method-specific idempotency option. `GET`, `PATCH`, and `DELETE` requests are not retried through the idempotency cache.
* **Always use idempotency keys** for create/action requests such as creating programs, invites, payouts, or marketing resources
* **Use business-meaningful keys** when possible (e.g., `payout_{affiliate_id}_{period}`) to prevent duplicates even across separate code paths
* **Retry with the same key** – don't generate a new key for retries
* **Keys expire after 24 hours** – after that, the same key can be reused
# Claim invite with authenticated API key owner
Source: https://agentref.co/docs/api-reference/invites/claim-invite-with-authenticated-api-key-owner
/openapi.json post /invites/{token}/claim
# Get invite details by public token
Source: https://agentref.co/docs/api-reference/invites/get-invite-details-by-public-token
/openapi.json get /invites/{token}
# Archive a marketing resource
Source: https://agentref.co/docs/api-reference/marketing-resources/archive-a-marketing-resource
/openapi.json post /marketing-resources/{resourceId}/archive
# Complete a social-post media upload
Source: https://agentref.co/docs/api-reference/marketing-resources/complete-a-social-post-media-upload
/openapi.json post /marketing-resources/social-posts/{resourceId}/media/complete
# Create a draft marketing social post
Source: https://agentref.co/docs/api-reference/marketing-resources/create-a-draft-marketing-social-post
/openapi.json post /programs/{id}/marketing-resources/social-posts
# Create a merchant marketing resource download URL
Source: https://agentref.co/docs/api-reference/marketing-resources/create-a-merchant-marketing-resource-download-url
/openapi.json post /marketing-resources/download-url
# Create a social-post media upload session
Source: https://agentref.co/docs/api-reference/marketing-resources/create-a-social-post-media-upload-session
/openapi.json post /marketing-resources/social-posts/{resourceId}/media/upload-sessions
# Create an affiliate marketing resource download URL
Source: https://agentref.co/docs/api-reference/marketing-resources/create-an-affiliate-marketing-resource-download-url
/openapi.json post /me/marketing-resources/download-url
# Get affiliate-visible marketing resource details
Source: https://agentref.co/docs/api-reference/marketing-resources/get-affiliate-visible-marketing-resource-details
/openapi.json get /me/marketing-resources/{resourceId}
# List affiliate-visible marketing resources
Source: https://agentref.co/docs/api-reference/marketing-resources/list-affiliate-visible-marketing-resources
/openapi.json get /me/programs/{programId}/marketing-resources
# List merchant marketing resources
Source: https://agentref.co/docs/api-reference/marketing-resources/list-merchant-marketing-resources
/openapi.json get /programs/{id}/marketing-resources
# Move a marketing resource back to draft
Source: https://agentref.co/docs/api-reference/marketing-resources/move-a-marketing-resource-back-to-draft
/openapi.json post /marketing-resources/{resourceId}/unpublish
# Notify approved affiliates about a marketing resource
Source: https://agentref.co/docs/api-reference/marketing-resources/notify-approved-affiliates-about-a-marketing-resource
/openapi.json post /marketing-resources/{resourceId}/notify
# Publish a marketing resource
Source: https://agentref.co/docs/api-reference/marketing-resources/publish-a-marketing-resource
/openapi.json post /marketing-resources/{resourceId}/publish
# Remove a social-post media attachment
Source: https://agentref.co/docs/api-reference/marketing-resources/remove-a-social-post-media-attachment
/openapi.json delete /marketing-resources/social-posts/{resourceId}/media/{mediaId}
# Render an affiliate social post with available links and disclosure guidance
Source: https://agentref.co/docs/api-reference/marketing-resources/render-an-affiliate-social-post-with-available-links-and-disclosure-guidance
/openapi.json post /me/marketing-resources/social-posts/{resourceId}/render
# Reorder social-post media attachments
Source: https://agentref.co/docs/api-reference/marketing-resources/reorder-social-post-media-attachments
/openapi.json post /marketing-resources/social-posts/{resourceId}/media/reorder
# Replace a social-post media file
Source: https://agentref.co/docs/api-reference/marketing-resources/replace-a-social-post-media-file
/openapi.json post /marketing-resources/social-posts/{resourceId}/media/{mediaId}/replace
# Update a marketing social post
Source: https://agentref.co/docs/api-reference/marketing-resources/update-a-marketing-social-post
/openapi.json patch /marketing-resources/social-posts/{resourceId}
# Update social-post media metadata
Source: https://agentref.co/docs/api-reference/marketing-resources/update-social-post-media-metadata
/openapi.json patch /marketing-resources/social-posts/{resourceId}/media/{mediaId}
# Apply to a marketplace program as authenticated affiliate
Source: https://agentref.co/docs/api-reference/marketplace/apply-to-a-marketplace-program-as-authenticated-affiliate
/openapi.json post /marketplace/apply/{programId}
# Discover public marketplace programs
Source: https://agentref.co/docs/api-reference/marketplace/discover-public-marketplace-programs
/openapi.json get /marketplace/programs
# Create a new affiliate link
Source: https://agentref.co/docs/api-reference/me/create-a-new-affiliate-link
/openapi.json post /me/links
# Delete an affiliate link
Source: https://agentref.co/docs/api-reference/me/delete-an-affiliate-link
/openapi.json delete /me/links/{id}
# Get affiliate program details for the authenticated key
Source: https://agentref.co/docs/api-reference/me/get-affiliate-program-details-for-the-authenticated-key
/openapi.json get /me/programs/{programId}
# Get affiliate workspace overview across programs
Source: https://agentref.co/docs/api-reference/me/get-affiliate-workspace-overview-across-programs
/openapi.json get /me/overview
# Get click stats for authenticated affiliate key
Source: https://agentref.co/docs/api-reference/me/get-click-stats-for-authenticated-affiliate-key
/openapi.json get /me/clicks
# Get current API key identity and owner overview
Source: https://agentref.co/docs/api-reference/me/get-current-api-key-identity-and-owner-overview
/openapi.json get /me
# Get earnings summary for authenticated affiliate key
Source: https://agentref.co/docs/api-reference/me/get-earnings-summary-for-authenticated-affiliate-key
/openapi.json get /me/earnings
# Get payout information for authenticated affiliate key
Source: https://agentref.co/docs/api-reference/me/get-payout-information-for-authenticated-affiliate-key
/openapi.json get /me/payout-info
# List affiliate links for authenticated key
Source: https://agentref.co/docs/api-reference/me/list-affiliate-links-for-authenticated-key
/openapi.json get /me/links
# List earnings entries for a specific program
Source: https://agentref.co/docs/api-reference/me/list-earnings-entries-for-a-specific-program
/openapi.json get /me/earnings/{programId}
# List payouts for authenticated affiliate key
Source: https://agentref.co/docs/api-reference/me/list-payouts-for-authenticated-affiliate-key
/openapi.json get /me/payouts
# List programs for the authenticated affiliate key
Source: https://agentref.co/docs/api-reference/me/list-programs-for-the-authenticated-affiliate-key
/openapi.json get /me/programs
# Update an affiliate link
Source: https://agentref.co/docs/api-reference/me/update-an-affiliate-link
/openapi.json patch /me/links/{id}
# Update payout information for authenticated affiliate key
Source: https://agentref.co/docs/api-reference/me/update-payout-information-for-authenticated-affiliate-key
/openapi.json patch /me/payout-info
# Get merchant account profile
Source: https://agentref.co/docs/api-reference/merchant/get-merchant-account-profile
/openapi.json get /merchant
# Get merchant notification preferences
Source: https://agentref.co/docs/api-reference/merchant/get-merchant-notification-preferences
/openapi.json get /merchant/notifications
# Update merchant account profile
Source: https://agentref.co/docs/api-reference/merchant/update-merchant-account-profile
/openapi.json patch /merchant
# Update merchant notification preferences
Source: https://agentref.co/docs/api-reference/merchant/update-merchant-notification-preferences
/openapi.json put /merchant/notifications
# Finalize onboarding (requires full key and active state)
Source: https://agentref.co/docs/api-reference/onboarding/finalize-onboarding-requires-full-key-and-active-state
/openapi.json post /onboarding/complete
# Set merchant profile data during onboarding
Source: https://agentref.co/docs/api-reference/onboarding/set-merchant-profile-data-during-onboarding
/openapi.json post /onboarding/merchant
# Create manual payout record
Source: https://agentref.co/docs/api-reference/payouts/create-manual-payout-record
/openapi.json post /payouts
# Get payout stats
Source: https://agentref.co/docs/api-reference/payouts/get-payout-stats
/openapi.json get /payouts/stats
# List affiliates eligible for payout
Source: https://agentref.co/docs/api-reference/payouts/list-affiliates-eligible-for-payout
/openapi.json get /payouts/pending
# List payouts
Source: https://agentref.co/docs/api-reference/payouts/list-payouts
/openapi.json get /payouts
# Archive program
Source: https://agentref.co/docs/api-reference/programs/archive-program
/openapi.json delete /programs/{id}
# Connect Stripe for a specific program
Source: https://agentref.co/docs/api-reference/programs/connect-stripe-for-a-specific-program
/openapi.json post /programs/{id}/connect-stripe
# Create coupon for a program
Source: https://agentref.co/docs/api-reference/programs/create-coupon-for-a-program
/openapi.json post /programs/{id}/coupons
# Create invite for a program
Source: https://agentref.co/docs/api-reference/programs/create-invite-for-a-program
/openapi.json post /programs/{id}/invites
# Create program
Source: https://agentref.co/docs/api-reference/programs/create-program
/openapi.json post /programs
# Disconnect Stripe for a specific program
Source: https://agentref.co/docs/api-reference/programs/disconnect-stripe-for-a-specific-program
/openapi.json delete /programs/{id}/connect-stripe
# Get program by id
Source: https://agentref.co/docs/api-reference/programs/get-program-by-id
/openapi.json get /programs/{id}
# Get program stats
Source: https://agentref.co/docs/api-reference/programs/get-program-stats
/openapi.json get /programs/{id}/stats
# Get tracking diagnostics status for a program
Source: https://agentref.co/docs/api-reference/programs/get-tracking-diagnostics-status-for-a-program
/openapi.json get /programs/{id}/tracking/status
# List affiliates for a program
Source: https://agentref.co/docs/api-reference/programs/list-affiliates-for-a-program
/openapi.json get /programs/{id}/affiliates
# List coupons for a program
Source: https://agentref.co/docs/api-reference/programs/list-coupons-for-a-program
/openapi.json get /programs/{id}/coupons
# List invites for a program
Source: https://agentref.co/docs/api-reference/programs/list-invites-for-a-program
/openapi.json get /programs/{id}/invites
# List programs
Source: https://agentref.co/docs/api-reference/programs/list-programs
/openapi.json get /programs
# Update marketplace visibility and listing details for a program
Source: https://agentref.co/docs/api-reference/programs/update-marketplace-visibility-and-listing-details-for-a-program
/openapi.json patch /programs/{id}/marketplace
# Update program
Source: https://agentref.co/docs/api-reference/programs/update-program
/openapi.json patch /programs/{id}
# Create an outbound webhook endpoint
Source: https://agentref.co/docs/api-reference/webhooks/create-an-outbound-webhook-endpoint
/openapi.json post /webhooks
# Disable an outbound webhook endpoint
Source: https://agentref.co/docs/api-reference/webhooks/disable-an-outbound-webhook-endpoint
/openapi.json delete /webhooks/{id}
# Get an outbound webhook endpoint by id
Source: https://agentref.co/docs/api-reference/webhooks/get-an-outbound-webhook-endpoint-by-id
/openapi.json get /webhooks/{id}
# List outbound webhook endpoints for the authenticated merchant
Source: https://agentref.co/docs/api-reference/webhooks/list-outbound-webhook-endpoints-for-the-authenticated-merchant
/openapi.json get /webhooks
# Rotate the signing secret for an outbound webhook endpoint
Source: https://agentref.co/docs/api-reference/webhooks/rotate-the-signing-secret-for-an-outbound-webhook-endpoint
/openapi.json post /webhooks/{id}/rotate-secret
# Update an outbound webhook endpoint
Source: https://agentref.co/docs/api-reference/webhooks/update-an-outbound-webhook-endpoint
/openapi.json patch /webhooks/{id}
# Agent Setup
Source: https://agentref.co/docs/getting-started/agent-setup
Connect AgentRef to AI assistants via MCP server, Node.js SDK, Python SDK, or REST API – manage your affiliate program through natural language.
AgentRef is agent-native. You can manage your affiliate program – creating programs, inviting affiliates, checking stats, processing payouts – through any AI assistant that supports tool use.
This page covers all four integration methods. Pick the one that fits your stack.
The MCP (Model Context Protocol) server is the fastest way to connect AgentRef to AI assistants. For Claude Code, Claude App, Codex, Cursor, and OpenClaw (`mcporter`), the recommended flow is OAuth-first: add the server once, finish the browser sign-in, and let your client store tokens for future sessions.
**Merchant server URL:** `https://www.agentref.co/api/mcp/merchant`
**Affiliate server URL:** `https://www.agentref.co/api/mcp/affiliate`
**Recommended auth:** MCP OAuth 2.1 with dynamic client registration
**Fallback auth for custom clients:** `Authorization: Bearer ak_live_your_key_here`
### Claude Code
```bash theme={null}
claude mcp add --scope user --transport http agentref-merchant https://www.agentref.co/api/mcp/merchant
```
Then open `/mcp` in Claude Code, select `agentref`, and complete the OAuth flow once.
### Claude App
1. Open `Customize -> Connectors -> + -> Add custom connector`
2. Enter `agentref` as the name
3. Enter `https://www.agentref.co/api/mcp/merchant` as the remote MCP server URL
4. Start a new conversation and send `please make a test call to the agentref mcp`
5. Confirm the tool request, then click **Reconnect** on the AgentRef connector card to authorize access
### Codex
Copy this prompt into Codex:
```text theme={null}
Please run `codex mcp add agentref-merchant --url https://www.agentref.co/api/mcp/merchant` in your shell.
```
Codex runs the command for you. Allow command execution once, then complete OAuth in the browser.
If AgentRef is already configured and you only need to re-authenticate:
```bash theme={null}
codex mcp login agentref-merchant
```
### Cursor
In `Settings -> Tools & MCP -> Add Custom MCP`, save this `mcp.json` entry:
```json theme={null}
{
"mcpServers": {
"agentref": {
"url": "https://www.agentref.co/api/mcp/merchant"
}
}
}
```
Then restart Cursor once, open the MCP settings again, and click **Connect** on the AgentRef server card.
### OpenClaw (`mcporter`)
Install or enable the `mcporter` skill in OpenClaw, then run:
```bash theme={null}
mcporter --config ~/.openclaw/workspace/config/mcporter.json config add agentref-merchant https://www.agentref.co/api/mcp/merchant --auth oauth
mcporter --config ~/.openclaw/workspace/config/mcporter.json auth --reset agentref-merchant
```
Approve the browser prompt. On success, the callback page says `Authorization successful. You can return to the CLI.`
### OpenClaw REST skill (fallback)
If you prefer OpenClaw without MCP, install the official skill from ClawHub:
```bash theme={null}
openclaw skills install agentref
```
Then create the appropriate AgentRef API key and store it in `~/.openclaw/.env` before restarting the gateway:
```bash theme={null}
grep -v '^AGENTREF_API_KEY=' ~/.openclaw/.env 2>/dev/null > /tmp/oc_env && printf 'AGENTREF_API_KEY=%s\n' 'ak_live_your_key_here' >> /tmp/oc_env && mv /tmp/oc_env ~/.openclaw/.env && openclaw gateway restart
```
### Other MCP clients
Any remote MCP client that supports OAuth for Streamable HTTP servers can use the same endpoint. Prefer OAuth when available. If your client only supports manual headers, use the same endpoint with `Authorization: Bearer ak_live_your_key_here`.
### Try it out
After connecting, try these prompts with your AI assistant:
* "Check my onboarding status and tell me what is still blocking launch"
* "Show me my affiliate program stats for the last 30 days"
* "List all my affiliates and their performance"
* "Create a new affiliate program called 'Partner Program' with 25% recurring commission"
* "Check if my tracking script is installed correctly"
The Node.js SDK provides a type-safe client for all AgentRef API operations.
### Installation
```bash theme={null}
npm install agentref
```
### Configuration
```typescript theme={null}
import { AgentRef } from 'agentref';
const client = new AgentRef({
apiKey: 'ak_live_your_key_here',
});
```
Store your API key in environment variables, not in source code. Use `process.env.AGENTREF_API_KEY` in production.
### Create a program
```typescript theme={null}
const program = await client.programs.create({
name: 'Partner Program',
commissionType: 'recurring',
commissionPercent: 25,
cookieDuration: 30,
payoutThreshold: 5000, // $50.00 in cents
payoutFrequency: 'monthly',
autoApproveAffiliates: true,
});
console.log(`Program created: ${program.id}`);
```
### Invite an affiliate
```typescript theme={null}
const invite = await client.programs.createInvite(program.id, {
email: 'partner@example.com',
});
console.log(`Invite sent: ${invite.id}`);
```
### Check stats
```typescript theme={null}
const stats = await client.programs.stats(program.id, {
period: '30d',
});
console.log(`Conversions: ${stats.total}`);
console.log(`Revenue: $${(stats.totalRevenue / 100).toFixed(2)}`);
```
### List conversions
```typescript theme={null}
const conversions = await client.conversions.list({
programId: program.id,
status: 'pending',
limit: 20,
});
for (const conversion of conversions.data) {
console.log(`${conversion.id}: $${(conversion.amount / 100).toFixed(2)}`);
}
```
For the full SDK reference, see [Node.js SDK](/docs/agent-integration/node-sdk).
The Python SDK provides a clean client for all AgentRef API operations.
### Installation
```bash theme={null}
pip install agentref
```
### Configuration
```python theme={null}
from agentref import AgentRef
client = AgentRef(api_key='ak_live_your_key_here')
```
Store your API key in environment variables, not in source code. Use `os.environ['AGENTREF_API_KEY']` in production.
### Create a program
```python theme={null}
program = client.programs.create(
name='Partner Program',
commission_type='recurring',
commission_percent=25,
cookie_duration=30,
payout_threshold=5000, # $50.00 in cents
payout_frequency='monthly',
auto_approve_affiliates=True,
)
print(f"Program created: {program.id}")
```
### Invite an affiliate
```python theme={null}
invite = client.programs.create_invite(
program.id,
email='partner@example.com',
)
print(f"Invite sent: {invite.id}")
```
### Check stats
```python theme={null}
stats = client.programs.stats(
program.id,
period='30d',
)
print(f"Conversions: {stats.total}")
print(f"Revenue: ${stats.total_revenue / 100:.2f}")
```
### List conversions
```python theme={null}
conversions = client.conversions.list(
program_id=program.id,
status='pending',
limit=20,
)
for conversion in conversions.data:
print(f"{conversion.id}: ${conversion.amount / 100:.2f}")
```
For the full SDK reference, see [Python SDK](/docs/agent-integration/python-sdk).
If you're not using an SDK or MCP, you can call the REST API directly.
**Base URL:** `https://www.agentref.co/api/v1`
**Auth:** Pass your API key as a Bearer token in the `Authorization` header.
### List programs
```bash cURL theme={null}
curl -X GET https://www.agentref.co/api/v1/programs \
-H "Authorization: Bearer ak_live_your_key_here"
```
```javascript fetch theme={null}
const res = await fetch('https://www.agentref.co/api/v1/programs', {
headers: { Authorization: 'Bearer ak_live_your_key_here' },
});
const data = await res.json();
```
```python requests theme={null}
import requests
res = requests.get(
'https://www.agentref.co/api/v1/programs',
headers={'Authorization': 'Bearer ak_live_your_key_here'},
)
data = res.json()
```
### Create a program
```bash cURL theme={null}
curl -X POST https://www.agentref.co/api/v1/programs \
-H "Authorization: Bearer ak_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Partner Program",
"commissionType": "recurring",
"commissionPercent": 25,
"cookieDuration": 30,
"payoutThreshold": 5000,
"payoutFrequency": "monthly",
"autoApproveAffiliates": true
}'
```
```javascript fetch theme={null}
const res = await fetch('https://www.agentref.co/api/v1/programs', {
method: 'POST',
headers: {
Authorization: 'Bearer ak_live_your_key_here',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Partner Program',
commissionType: 'recurring',
commissionPercent: 25,
cookieDuration: 30,
payoutThreshold: 5000,
payoutFrequency: 'monthly',
autoApproveAffiliates: true,
}),
});
const program = await res.json();
```
```python requests theme={null}
import requests
res = requests.post(
'https://www.agentref.co/api/v1/programs',
headers={
'Authorization': 'Bearer ak_live_your_key_here',
'Content-Type': 'application/json',
},
json={
'name': 'Partner Program',
'commissionType': 'recurring',
'commissionPercent': 25,
'cookieDuration': 30,
'payoutThreshold': 5000,
'payoutFrequency': 'monthly',
'autoApproveAffiliates': True,
},
)
program = res.json()
```
### Get stats
```bash cURL theme={null}
curl -X GET https://www.agentref.co/api/v1/programs/PROGRAM_ID/stats \
-H "Authorization: Bearer ak_live_your_key_here"
```
```javascript fetch theme={null}
const res = await fetch('https://www.agentref.co/api/v1/programs/PROGRAM_ID/stats', {
headers: { Authorization: 'Bearer ak_live_your_key_here' },
});
const programStats = await res.json();
```
```python requests theme={null}
res = requests.get(
'https://www.agentref.co/api/v1/programs/PROGRAM_ID/stats',
headers={'Authorization': 'Bearer ak_live_your_key_here'},
)
program_stats = res.json()
```
For the complete API reference, see [API Reference](/docs/api-reference/authentication).
## Copy for AI assistants
If your AI assistant does not support MCP, point it to `https://www.agentref.co/docs/llms-full.txt` first. That gives it the full published AgentRef documentation as plain Markdown.
```text theme={null}
AgentRef docs: https://www.agentref.co/docs
Full docs for LLMs: https://www.agentref.co/docs/llms-full.txt
REST base URL: https://www.agentref.co/api/v1
MCP merchant endpoint: https://www.agentref.co/api/mcp/merchant
MCP affiliate endpoint: https://www.agentref.co/api/mcp/affiliate
Preferred MCP auth: OAuth 2.1 + dynamic client registration
Fallback MCP auth: Authorization: Bearer ak_live_* / ak_aff_*
Current MCP workflow groups:
- merchant_setup: get_onboarding_status, update_merchant_profile, create_program, list_programs, update_program, get_stripe_connect_url, complete_onboarding, get_tracking_snippet, verify_tracking
- merchant_growth: list_affiliates, list_applications, review_application, set_affiliate_status, list_invites, create_invite, revoke_invite, get_program_overview, list_conversions, list_flags, review_flag
- merchant_payouts: list_pending_payouts, list_payouts, list_upcoming_payouts, create_payout, update_payout_status, export_payouts_csv
- merchant_marketing_resources: list_marketing_resources, get_marketing_resource, create_marketing_collection, update_marketing_collection, publish_marketing_resource, unpublish_marketing_resource, archive_marketing_resource, create_marketing_social_post, update_marketing_social_post, create_marketing_social_post_media_upload_session, complete_marketing_social_post_media_upload, remove_marketing_social_post_media, update_marketing_social_post_media, reorder_marketing_social_post_media, replace_marketing_social_post_media, create_marketing_resource_download_url, create_marketing_external_link, create_marketing_upload_session, complete_marketing_upload_session, import_marketing_resource_from_url, notify_marketing_resource_affiliates
- affiliate_workspace: discover_programs, apply_to_program, get_affiliate_overview, list_my_programs, list_links, create_link, get_earnings, get_click_stats, list_my_payouts, update_my_payout_profile
- affiliate_marketing_resources: list_marketing_resources, get_marketing_resource, render_marketing_social_post, create_marketing_resource_download_url
Resources:
merchant://me
program://{id}
stats://{programId}
affiliate://me/programs
marketplace://catalog
marketing-resources://program/{program_id}
marketing-resource-collection://{collection_id}
marketing-resource://{resource_id}
Prompts:
onboard_program
review_payout_run
debug_tracking_installation
triage_fraud_flags
```
## MCP workflow groups
AgentRef's MCP release surface is organized into workflow groups with snake\_case inputs and outputs. Mutation tools accept `idempotency_key` when retries need to be safe. MCP includes some workflows that are broader than REST/SDK, especially onboarding status, tracking snippet/verify, invite revoke, payout exports/status updates, and advanced Marketing Resources operations.
### `merchant_setup`
* `get_onboarding_status`
* `update_merchant_profile`
* `create_program`
* `list_programs`
* `update_program`
* `get_stripe_connect_url`
* `complete_onboarding`
* `get_tracking_snippet`
* `verify_tracking`
### `merchant_growth`
* `list_affiliates`
* `list_applications`
* `review_application`
* `set_affiliate_status`
* `list_invites`
* `create_invite`
* `revoke_invite`
* `get_program_overview`
* `list_conversions`
* `list_flags`
* `review_flag`
### `merchant_payouts`
* `list_pending_payouts`
* `list_payouts`
* `list_upcoming_payouts`
* `create_payout`
* `update_payout_status`
* `export_payouts_csv`
### `merchant_marketing_resources`
* `list_marketing_resources`
* `get_marketing_resource`
* `create_marketing_collection`
* `update_marketing_collection`
* `publish_marketing_resource`
* `unpublish_marketing_resource`
* `archive_marketing_resource`
* `create_marketing_social_post`
* `update_marketing_social_post`
* `create_marketing_social_post_media_upload_session`
* `complete_marketing_social_post_media_upload`
* `remove_marketing_social_post_media`
* `update_marketing_social_post_media`
* `reorder_marketing_social_post_media`
* `replace_marketing_social_post_media`
* `create_marketing_resource_download_url`
* `create_marketing_external_link`
* `create_marketing_upload_session`
* `complete_marketing_upload_session`
* `import_marketing_resource_from_url`
* `notify_marketing_resource_affiliates`
### `affiliate_workspace`
* `discover_programs`
* `apply_to_program`
* `get_affiliate_overview`
* `list_my_programs`
* `list_links`
* `create_link`
* `get_earnings`
* `get_click_stats`
* `list_my_payouts`
* `update_my_payout_profile`
### `affiliate_marketing_resources`
* `list_marketing_resources`
* `get_marketing_resource`
* `render_marketing_social_post`
* `create_marketing_resource_download_url`
See [MCP Server Deep Dive](/docs/agent-integration/mcp-server) for the full tool, resource, prompt, and setup reference.
## What's next
Advanced MCP configuration, scopes, and resource access.
Full SDK reference with all methods and types.
Full SDK reference for Python integrations.
# How It Works
Source: https://agentref.co/docs/getting-started/how-it-works
The full attribution chain – from affiliate click to conversion, commission calculation, fraud detection, and payout.
AgentRef handles the complete affiliate lifecycle: tracking clicks, attributing conversions, calculating commissions, detecting fraud, and managing payouts. This page walks through each stage.
## The attribution chain
Every conversion follows this path:
```mermaid theme={null}
graph TD
A["Affiliate shares referral link"] --> B["Visitor clicks link"]
B --> C["AgentRef records click + sets cookie"]
C --> D["Visitor browses your site"]
D --> E["Visitor completes purchase via Stripe"]
E --> F["Stripe webhook fires"]
F --> G["AgentRef matches cookie to affiliate"]
G --> H["Conversion created (pending)"]
H --> I["Commission calculated"]
I --> J["Fraud checks run"]
J --> K["Commission approved"]
K --> L["Payout when threshold reached"]
```
## Click tracking
When a visitor clicks an affiliate's referral link, AgentRef's tracking script handles attribution automatically:
1. **Referral link redirect.** The visitor clicks a link like `https://www.agentref.co/r/abc123`. AgentRef resolves the affiliate code, records the click, and redirects to the target URL.
2. **Cookie placement.** The tracking script sets first-party cookies on your domain:
* `agentref_cid` – a click identifier for conversion matching
* `agentref_src` – the traffic source
* `agentref_pid` – the active program ID
3. **Cookie duration.** Cookies persist for your configured duration (default: 30 days). If a visitor returns within that window and converts, the affiliate still gets credit.
4. **Click deduplication.** Multiple clicks from the same visitor within a short window are deduplicated to prevent inflated metrics.
The tracking script is a lightweight, first-party JavaScript snippet that runs on your domain. It does not use third-party cookies or fingerprinting.
## Conversion attribution
When a tracked visitor makes a purchase, AgentRef connects the sale to the affiliate:
1. **Stripe webhook.** After a successful payment, Stripe sends a `checkout.session.completed` (or `invoice.paid` for subscriptions) webhook to AgentRef.
2. **Metadata matching.** AgentRef looks for the `agentref_cid` in the Stripe session metadata. This links the payment back to the original click.
3. **Conversion record.** A conversion is created with status `pending`. It includes the revenue amount, the affiliate ID, and the click that drove the sale.
4. **Last-click attribution.** If multiple affiliates drove clicks for the same visitor, the most recent click before conversion wins. This is the industry-standard attribution model.
### How metadata flows into Stripe
For **Stripe Payment Links, Buy Buttons, and Pricing Tables**, the tracking script automatically instruments the hosted Stripe surface with the click token via `client_reference_id` – no server code needed.
For **server-created Checkout Sessions**, you read the metadata from the tracking script on the client side and pass it to your server:
```javascript theme={null}
// Client side – read captured referral context
const metadata = window.AgentRef.getCheckoutMetadata();
// Returns: { agentref_cid: "clk_abc123", agentref_pid: "550e8400-e29b-41d4-a716-446655440000", agentref_source: "redirect" }
// Server side – include in Stripe session creation
const session = await stripe.checkout.sessions.create({
// ...your config
metadata: metadata,
subscription_data: { metadata: metadata }, // for subscriptions
});
```
## Commission calculation
Once a conversion is recorded, AgentRef calculates the affiliate's commission based on your program settings:
| Commission type | How it works |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Percentage (one-time)** | Affiliate earns a percentage of the sale amount. Example: 20% of a $100 sale = $20 commission. |
| **Percentage (recurring)** | Affiliate earns a percentage on every subscription payment. Continues for the lifetime of the subscription or until a configured limit. |
| **Recurring with limit** | Same as recurring, but capped at a number of months. Example: 20% for the first 12 months. |
Commissions are calculated in the currency of the Stripe payment. AgentRef supports multi-currency programs.
## Fraud detection
Every conversion passes through automated fraud checks before commissions are approved:
* **Duplicate click detection.** Flags unusual click patterns from the same IP or device within a short timeframe.
* **Self-referral detection.** Catches affiliates who try to refer themselves by matching affiliate identity against the buyer.
* **Suspicious conversion patterns.** Identifies statistically unusual spikes in conversions that deviate from an affiliate's normal baseline.
* **Geographic anomalies.** Flags when click geography and conversion geography don't match expected patterns.
Flagged conversions are held for review. You can resolve flags through the dashboard, API, or MCP server using the `review_flag` tool.
Most legitimate conversions pass fraud checks automatically. Flags are designed to surface genuine anomalies, not to create false positives for normal affiliate activity.
## Payouts
When an affiliate's approved commissions reach the payout threshold, they become eligible for payout:
1. **Threshold check.** Default payout threshold is \$50 (configurable per program). Commissions accumulate until this amount is reached.
2. **Payout schedule.** Payouts are processed on your configured frequency: weekly, monthly, or quarterly.
3. **Payout execution.** AgentRef creates the payout record and handles the commission state transitions. You complete the actual payment through your preferred method (Stripe, PayPal, Wise, bank transfer).
4. **CSV export.** For bulk payouts, export a CSV in PayPal, Wise, SEPA, or generic format using the MCP `export_payouts_csv` tool.
### Conversion status flow
Every conversion moves through a defined lifecycle:
```mermaid theme={null}
graph LR
A[pending] --> B[approved]
A --> C[rejected]
B --> D[paid]
A --> E[refunded]
B --> F[partially_refunded]
F --> E
B --> E
```
* **Pending** – Conversion recorded, awaiting fraud checks and approval window.
* **Approved** – Passed all checks. Commission is locked in and counts toward payout.
* **Rejected** – Failed fraud checks or manually rejected by merchant.
* **Paid** – Commission has been included in a completed payout.
* **Partially refunded** – Part of the Stripe payment was refunded. Commission is reduced proportionally.
* **Refunded** – The original Stripe payment was fully refunded. Commission is reversed.
## Integration points
AgentRef provides multiple ways to integrate with your stack:
| Integration | Purpose | Best for |
| ----------------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| **Tracking script** | Captures clicks and sets attribution cookies on your domain. | All merchants – required for click tracking. |
| **Stripe webhooks** | Automatically creates conversions from Stripe payment events. | All merchants using Stripe. |
| **REST API** | Full programmatic access to all AgentRef resources. | Custom integrations, backend automation. |
| **MCP server** | OAuth-ready tools, resources, and prompts for AI assistants. | Claude, Codex, Cursor, OpenClaw, and custom agent workflows. |
| **Webhooks (outgoing)** | Receive real-time notifications for 13 event types (program, affiliate, conversion, payout, flag events). | Custom workflows, CRM sync, notifications. |
| **Node.js SDK** | Type-safe API client for JavaScript/TypeScript projects. | Node.js backends and scripts. |
| **Python SDK** | API client for Python projects. | Python backends, data analysis, automation scripts. |
## Next steps
Set up your first program in 5 minutes.
Connect AgentRef to your AI assistant via MCP.
# Quickstart
Source: https://agentref.co/docs/getting-started/quickstart
Set up your first AgentRef affiliate program in under 5 minutes – from account creation to your first API call.
This guide walks you through setting up a complete affiliate program. By the end, you'll have tracking installed, an API key ready, and a working integration.
## Prerequisites
* A [Stripe account](https://stripe.com) (for payment tracking)
* A website where you sell your product (for the tracking script)
## Setup
Sign up at [agentref.co](https://www.agentref.co). No credit card required.
After signing up, you'll land on the onboarding flow that walks you through the next steps.
Click **Connect Stripe** in the onboarding flow. You'll be redirected to Stripe's OAuth page to authorize AgentRef.
AgentRef uses Stripe Connect to:
* Listen for payment events (checkout completions, invoice payments, refunds)
* Match payments to affiliate referrals
* Track subscription renewals for recurring commissions
AgentRef needs read access to your Stripe payment events. It does not process payments or access your bank account.
After connecting Stripe, create your first affiliate program. Here are recommended starting settings:
| Setting | Recommendation | Why |
| --------------------------- | -------------- | ---------------------------------------------------------------------------- |
| **Commission type** | `recurring` | Incentivizes affiliates to refer customers who stick around. |
| **Commission rate** | 20–30% | Industry standard for SaaS. Competitive enough to attract affiliates. |
| **Cookie duration** | 30 days | Balances fair attribution with reasonable conversion windows. |
| **Payout threshold** | \$50 | Low enough to keep affiliates motivated, high enough to avoid micro-payouts. |
| **Payout frequency** | Monthly | Standard cadence. Weekly is fine for high-volume programs. |
| **Auto-approve affiliates** | Yes | Reduces friction. You can always block bad actors later. |
You can change all of these settings later. Start simple and adjust based on real data.
Add the AgentRef tracking script to your website. Place it in your `` tag or before the closing `` tag:
```html theme={null}
```
Replace `YOUR_PROGRAM_ID` with the ID shown in your program settings.
The script handles:
* Capturing affiliate referral codes from URL parameters
* Setting first-party attribution cookies (`agentref_cid`, `agentref_pid`, `agentref_src`)
* Providing `getCheckoutMetadata()` for Stripe session integration
**For Stripe Payment Links / Buy Buttons / Pricing Tables:** No additional code needed. The tracking script instruments hosted Stripe surfaces with the click token via `client_reference_id`.
**For server-created Checkout Sessions:** Pass the metadata to your server:
```javascript Client-side theme={null}
// Read referral context captured by the tracking script
const metadata = window.AgentRef.getCheckoutMetadata();
// Send metadata to your server along with the checkout request
```
```javascript Server-side (Node.js) theme={null}
const session = await stripe.checkout.sessions.create({
line_items: [{ price: 'price_xxx', quantity: 1 }],
mode: 'subscription',
metadata: metadata, // top-level metadata
subscription_data: { metadata: metadata }, // subscription metadata
success_url: 'https://yoursite.com/success',
cancel_url: 'https://yoursite.com/cancel',
});
```
Go to **Settings > API Keys** in your AgentRef dashboard and create a new key.
* **Merchant keys** start with `ak_live_` and can access your programs, applications, affiliates, conversions, payouts, webhooks, and marketing resources based on selected scopes.
Keep your API key secret. Never expose it in client-side code, public repositories, or browser requests.
Verify everything works by fetching your program stats:
```bash cURL theme={null}
curl -X GET https://www.agentref.co/api/v1/programs \
-H "Authorization: Bearer ak_live_your_key_here"
```
```javascript Node.js theme={null}
import { AgentRef } from 'agentref';
const client = new AgentRef({ apiKey: 'ak_live_your_key_here' });
const programs = await client.programs.list();
console.log(programs);
```
```python Python theme={null}
from agentref import AgentRef
client = AgentRef(api_key='ak_live_your_key_here')
programs = client.programs.list()
print(programs)
```
You should see your program in the response. If you get a `401` error, double-check your API key.
## Verify your setup
After completing the steps above, confirm everything is working:
1. **Tracking script loaded.** Open your website, open browser DevTools, and check the Network tab for a request to `agentref.co/api/tracking/script.js`. It should return `200`.
2. **Cookies set.** Visit your site through a test referral link (create one in the dashboard). Check DevTools > Application > Cookies for `agentref_cid` and `agentref_pid`.
3. **API responds.** The API call from the previous step returns your program data.
3. **Stripe connected.** Your AgentRef dashboard shows the Stripe connection as active.
Use the Merchant MCP `verify_tracking` tool for an agent-assisted setup check, or call `GET /api/v1/programs/{id}/tracking/status` to inspect tracking health from REST/SDK.
## What's next
Connect AgentRef to Claude, Codex, Cursor, OpenClaw, or any AI assistant.
Start recruiting affiliates to your program.
Publish campaign material for affiliates.
Explore the full REST API documentation.
# Welcome to AgentRef
Source: https://agentref.co/docs/getting-started/welcome
Affiliate tracking for SaaS – agent-compatible from day one.
Pay nothing until you earn.
AgentRef is affiliate tracking software for SaaS. It connects your Stripe account, tracks referral clicks, attributes conversions, and pays affiliates – all with zero upfront cost.
Start free, then upgrade when your program needs higher limits.
No per-seat pricing.
## Get started
Set up your first affiliate program in 5 minutes.
Connect AgentRef to Claude, Codex, Cursor, OpenClaw, or any AI agent via MCP.
Explore the REST API, SDKs, and authentication.
## Why AgentRef?
Most affiliate platforms charge \$50–500/month before you have a single affiliate. AgentRef flips that model:
* **\$0 to start.** No credit card required. Create your program, invite affiliates, and go live immediately.
* **Program-friendly pricing.** Start free and move to a paid plan when your affiliate program needs higher limits.
* **Agent-compatible.** Manage your affiliate program through AI assistants using the MCP server, SDKs, or REST API when it fits your workflow.
## Core concepts
Before diving in, here are the terms you'll see throughout these docs:
| Concept | What it means |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Program** | Your affiliate program. It defines commission rates, cookie duration, payout rules, and tracking settings. One merchant can run multiple programs. |
| **Affiliate** | A partner who promotes your product using unique referral links. They earn commissions on conversions they drive. |
| **Click** | A tracked visit through an affiliate's referral link. AgentRef records the click and drops a first-party cookie for attribution. |
| **Conversion** | A successful sale or signup attributed to an affiliate. Created automatically from supported attribution sources such as Stripe metadata, Stripe `client_reference_id`, or coupon attribution. |
| **Commission** | The amount an affiliate earns for a conversion. Can be a percentage of revenue, a fixed amount, or recurring for subscription products. |
| **Payout** | The transfer of accumulated commissions to an affiliate once they hit the payout threshold. |
| **Marketing Resource** | Merchant-published campaign material such as files, social posts, swipe copy, external links, and collections. |
## How it fits together
```mermaid theme={null}
graph LR
A[Affiliate shares link] --> B[Visitor clicks link]
B --> C[AgentRef tracks click]
C --> D[Visitor buys on your site]
D --> E[Stripe webhook fires]
E --> F[AgentRef creates conversion]
F --> G[Commission attributed]
G --> H[Payout when threshold met]
```
1. You create a **program** with your commission structure.
2. **Affiliates** join and share referral links.
3. AgentRef's tracking script captures **clicks** and sets attribution cookies.
4. When a visitor converts (via Stripe), AgentRef matches the **conversion** to the affiliate.
5. **Commissions** accumulate until the payout threshold is reached.
6. **Payouts** are processed on your chosen schedule.
## What makes AgentRef different
OAuth-ready MCP server with setup, growth, payout, marketing resources, and affiliate workspace tools. Your AI assistant can operate AgentRef through natural language.
No monthly fees, no setup costs. You only pay when affiliates actually drive revenue for your business.
Connects directly to your Stripe account. Conversions are tracked automatically through webhooks – no manual reporting.
Supports recurring commissions for subscriptions, handles refunds and chargebacks, and scales from day one.
## Next steps
Understand the full attribution chain from click to payout.
Get your first program running in under 5 minutes.
## Choose your integration surface
| Surface | Use it when | Notes |
| ---------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Dashboard | You are launching or operating a program manually. | Best for setup, visual review, billing, and day-to-day operations. |
| REST API | You are building a server-side integration. | Stable public API v1 with OpenAPI reference, API keys, scopes, idempotency, and webhooks. |
| Node/Python SDKs | You want typed REST clients in application code. | Mirrors the REST API surface and handles auth, retries, errors, and pagination helpers. |
| MCP Server | You want an AI agent to run AgentRef workflows. | Broadest automation surface; includes setup, payout, and Marketing Resources workflows that are not all exposed in REST/SDK yet. |
If you are unsure, start with the dashboard and tracking quickstart. Use REST or an SDK for app code, and MCP when an AI assistant should operate AgentRef directly.
# Astro Integration
Source: https://agentref.co/docs/integration/astro
Install AgentRef in a shared Astro layout so affiliate tracking works across your marketing site or app shell.
Astro is a good fit for AgentRef because the tracking script can live in one shared layout and still cover static pages, islands, and Stripe embeds.
## Before you start
* Set the program website in AgentRef to the same root domain your Astro site uses.
* Copy the exact script snippet from `Settings -> Integration`.
* If checkout starts on a sibling subdomain, install the script there too.
## Recommended installation
Put the script in the shared layout you use across public pages.
```astro theme={null}
---
export interface Props {
title: string;
}
const { title } = Astro.props;
---
{title}
```
You do not need a separate Framer-style helper script in Astro. AgentRef already watches the DOM for late-rendered Stripe links, Buy Buttons, and Pricing Tables.
## Checkout compatibility in Astro
| Checkout pattern | Recommendation |
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Stripe Payment Links, Buy Buttons, or Pricing Tables embedded in Astro pages | Supported directly. |
| Custom server-created Stripe Checkout Session | Supported, but you must pass AgentRef metadata into Stripe on the server. |
| External checkout on another root domain | Not automatic. Keep checkout on the same root or build an intentional server-side handoff. |
## Multi-domain and consent notes
* Shared subdomains on one root can work well if AgentRef is installed everywhere the journey can begin.
* If you use a consent banner script, call `window.AgentRef.setConsent('granted')` after opt-in.
* If you run a strict CSP, allow `https://www.agentref.co` for `script-src` and `connect-src`.
## Verify the installation
Make sure the layout change is on the deployed site, not just in local preview.
Use a real affiliate link and confirm the AgentRef cookies appear.
Add `?agentref_debug=1` and inspect the console output.
Use the tracking health panel or `verify_tracking`.
## Troubleshooting
* The script exists in the layout but cookies never appear: check whether the program website in AgentRef matches the live domain.
* Clicks arrive but Stripe conversions do not: your checkout backend still needs the [Stripe guide](/docs/integration/stripe).
* The script works locally but not in production: verify the deployed layout actually includes the script and that no CSP blocks it.
## Related docs
Metadata bridge for custom Stripe sessions.
Full tracking-script reference.
Inspect hosted Stripe instrumentation and warnings.
# Framer Integration
Source: https://agentref.co/docs/integration/framer
Install AgentRef on Framer and use Stripe-hosted surfaces without the extra helper scripts other tools often require.
Framer is the guide where AgentRef differs most clearly from other affiliate tools: you only need the main tracking script. AgentRef already handles late-rendered Stripe links, Buy Buttons, and Pricing Tables by itself.
## Before you start
* Set the program website in AgentRef to the same root domain as your Framer site.
* Copy the exact script snippet from `Settings -> Integration`.
* Decide whether you use Stripe-hosted surfaces or a custom checkout flow.
## Install the script in Framer
1. Open your Framer project.
2. Go to `Site Settings -> General -> Custom Code`.
3. Paste the AgentRef script into the `End of ` field.
```html theme={null}
```
4. Publish the site.
## What makes AgentRef different on Framer
Other tools often need extra body scripts in Framer to rewrite Stripe links after the page renders. AgentRef already does that internally.
That means:
* no separate Stripe Payment Link enhancer script
* no separate Pricing Table enhancer script
* no separate Buy Button enhancer script
As long as the AgentRef script is loaded on the page, it will observe the DOM and instrument supported Stripe-hosted surfaces automatically.
## Checkout compatibility on Framer
| Flow | Status with AgentRef |
| ----------------------------------------------------------------------------- | ------------------------------------------------------------ |
| Stripe Payment Links, Buy Buttons, or Pricing Tables directly on Framer pages | Supported directly. |
| Custom Stripe checkout handled by your own backend or app | Supported with the [Stripe guide](/docs/integration/stripe). |
| External commerce tool that owns checkout without a Stripe metadata bridge | Not automatic. |
## Multi-domain and consent notes
* If Framer is just the marketing layer and checkout begins on `app.example.com`, install AgentRef on both surfaces.
* Different root domains do not share first-party cookies automatically.
* If you use a consent manager, call `window.AgentRef.setConsent('granted')` after opt-in.
## Verify the installation
Framer custom code only matters on the published site.
Open a live affiliate link and confirm `agentref_cid` and `agentref_pid`.
If the page contains Stripe links or elements, use `?agentref_debug=1` and inspect the console or DOM.
If you create custom Checkout Sessions elsewhere, follow the [Stripe guide](/docs/integration/stripe).
## Troubleshooting
* Payment Links are still plain after publish: confirm the AgentRef script actually loads on the same page as the links.
* The Framer page records clicks but conversions never appear: your checkout path is probably outside Framer and still needs the Stripe metadata bridge.
* Cookies do not survive from Framer to the app: keep both on one root domain and install AgentRef on both.
## Related docs
Use this if Framer hands off into your own Stripe session-creation backend.
Inspect hosted Stripe instrumentation and runtime warnings.
Full script behavior and DOM API reference.
# Installation Guides
Source: https://agentref.co/docs/integration/index
Choose the right AgentRef installation guide for your framework, site builder, or Stripe checkout setup.
AgentRef only needs one tracking script, but the correct installation point depends on your stack. This section gives you a platform-specific guide for each framework and builder linked from the AgentRef dashboard.
## The AgentRef contract in one minute
1. Install the script on every page where affiliate traffic can land or where checkout can start.
2. Set the program website in AgentRef to the same root domain that serves the script.
3. Keep the user journey on the same root domain when possible. `www.example.com` and `app.example.com` work well together. Different roots like `example.com` and `example-store.com` do not share first-party cookies.
4. If you use Stripe Payment Links, Buy Buttons, or Pricing Tables on the same page, the script is usually enough.
5. If you create Stripe Checkout Sessions yourself, read `window.AgentRef.getCheckoutMetadata()` and pass it into Stripe metadata. See the Stripe guide below.
6. If your buy button lives in a Chrome extension, desktop app, mobile app, or external dashboard, route it through a checkout start page on the website where AgentRef is installed before redirecting to Stripe.
7. If a platform uses its own native ecommerce checkout, do not assume automatic attribution. Several builder guides call this out explicitly.
Copy the exact script snippet from Settings -> Integration in AgentRef. It already includes your real program ID.
## Framework guides
App Router and Pages Router installation for full-site tracking.
Install once in your app shell without duplicate script injection.
Recommended setup for Vue SPAs and static Vue sites.
Add AgentRef through nuxt.config.ts or a shared layout.
Install in a reusable Astro layout that covers all landing pages.
SvelteKit and plain Svelte installation options.
## Website builder guides
Theme, plugin, and child-theme options for site-wide tracking.
Theme installation plus a clear note about Shopify native checkout limits.
Site-wide custom code setup for published Webflow sites.
Framer custom-code setup without extra helper scripts.
Add the script through Wix custom code on all published pages.
Use code injection for a global AgentRef install.
## Stripe guide
Covers hosted Stripe surfaces, custom Checkout Sessions, external app checkout routing, metadata, and verification.
## Verify any installation
Use a live test link from AgentRef so the visitor arrives with a real referral parameter like ?via=abc123.
In browser DevTools, check that agentref\_cid and agentref\_pid exist after the page loads.
Add ?agentref\_debug=1 to the URL and inspect the browser console. You can also use ?agentref\_verify=1 on a live page to trigger a tracking-install verification hit.
Use the tracking health panel, the REST endpoint at /api/v1/programs/:id/tracking/status, or the MCP verify\_tracking tool to confirm that the script install was seen and clicks are arriving.
## Related docs
Core script behavior, cookies, parameters, and the DOM API.
How to gate AgentRef until the visitor grants consent.
Inspect runtime state, warnings, and hosted Stripe instrumentation.
# Next.js Integration
Source: https://agentref.co/docs/integration/nextdotjs
Install the AgentRef tracking script in a Next.js app and keep affiliate attribution working across App Router or Pages Router.
Next.js is one of the cleanest AgentRef integrations because you can install the tracking script once in a shared layout and cover the whole app.
## Before you start
* In AgentRef, set the program website to the same root domain your Next.js app serves.
* Copy the exact script snippet from `Settings -> Integration`.
* If you split traffic between `www.example.com` and `app.example.com`, install the script on both surfaces if either one can receive affiliate traffic or start checkout.
## Recommended installation
Use `next/script` in `app/layout.tsx` so the script loads once for the whole application.
```tsx theme={null}
import Script from 'next/script';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
{children}
);
}
```
If you still use the Pages Router, install the script in `_app.tsx` or `_document.tsx`. `_app.tsx` is usually the simplest choice.
```tsx theme={null}
import type { AppProps } from 'next/app';
import Script from 'next/script';
export default function App({ Component, pageProps }: AppProps) {
return (
<>
>
);
}
```
Do not append the script from a React effect unless you have a strong reason. In Next.js, `next/script` is the cleanest and least error-prone installation path.
## Checkout compatibility in Next.js
| Checkout pattern | Recommendation |
| --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Stripe Payment Links, Buy Buttons, or Pricing Tables rendered in your app | Supported directly. Keep the AgentRef script on the same page. |
| Server-created Checkout Sessions in API routes, server actions, or route handlers | Supported, but you must bridge `window.AgentRef.getCheckoutMetadata()` into Stripe metadata. Use the [Stripe guide](/docs/integration/stripe). |
| Checkout starts on a different root domain | First-party cookies will not carry across roots. Move the flow onto one root or build a server-side handoff intentionally. |
## Consent, CSP, and multi-domain notes
* If you use a cookie banner, call `window.AgentRef.setConsent('granted')` after the visitor accepts tracking. See [Consent and GDPR](/docs/tracking/consent-gdpr).
* If you use a Content Security Policy, allow `https://www.agentref.co` in `script-src` and `connect-src`.
* If your marketing site and app live on sibling subdomains, install AgentRef on both when both can begin the journey.
## Verify the installation
Open a live test link from AgentRef and land on your Next.js app.
Confirm that `agentref_cid` and `agentref_pid` exist in DevTools.
Add `?agentref_debug=1` to the URL and inspect the browser console.
Open the tracking health panel or call `/api/v1/programs/:id/tracking/status`.
## Troubleshooting
* `window.AgentRef` is undefined: the script has not loaded yet. Trigger checkout only after the page is interactive.
* Cookies exist on `www` but not in your app subdomain: install the script on both subdomains and make sure the program website points at the shared root.
* Clicks are recorded but conversions are missing: your Next.js checkout path probably creates Stripe sessions server-side and still needs the [Stripe metadata bridge](/docs/integration/stripe).
## Related docs
Custom Checkout Session metadata bridge for Next.js backends.
Full cookie, parameter, and API behavior.
Gate tracking until the visitor grants consent.
# Nuxt Integration
Source: https://agentref.co/docs/integration/nuxt
Install AgentRef in Nuxt with a global script definition that covers every route.
Nuxt is a strong fit for AgentRef because you can register the tracking script globally and let it stay active across route changes.
## Before you start
* Set the program website in AgentRef to the same root domain your Nuxt app uses.
* Copy the exact script snippet from `Settings -> Integration`.
* If checkout starts on another subdomain, install AgentRef there too.
## Recommended installation
The cleanest Nuxt installation is a global head script in `nuxt.config.ts`.
```ts theme={null}
export default defineNuxtConfig({
app: {
head: {
script: [
{
src: 'https://www.agentref.co/api/tracking/script.js?pid=YOUR_PROGRAM_ID',
defer: true,
},
],
},
},
});
```
If you need to scope installation through a shared layout, use `useHead()` in `app.vue` or a global layout.
```vue theme={null}
```
## Checkout compatibility in Nuxt
| Checkout pattern | Recommendation |
| ------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| Hosted Stripe surfaces on Nuxt pages | Supported directly. |
| Nuxt server routes or backend create Checkout Sessions | Supported, but you must bridge `window.AgentRef.getCheckoutMetadata()` into Stripe metadata. |
| Cross-root-domain checkout | Not automatic. First-party cookies stay on one root domain. |
## Nuxt-specific notes
* Install once globally instead of per page.
* AgentRef works well with route changes because the script remains loaded after hydration.
* If you enable consent mode, call `window.AgentRef.setConsent('granted')` from your banner integration after opt-in.
## Verify the installation
1. Visit the Nuxt app through a live affiliate link.
2. Confirm `agentref_cid` and `agentref_pid` in browser cookies.
3. Add `?agentref_debug=1` and inspect the console.
4. Complete one supported Stripe test checkout and confirm AgentRef records the conversion.
## Troubleshooting
* The script only loads on one route: move it into `nuxt.config.ts` or a shared app-level layout.
* Clicks appear but no conversion does: your custom session path still needs the [Stripe guide](/docs/integration/stripe).
* Consent is required but cookies never appear: your banner integration probably never calls `window.AgentRef.setConsent('granted')`.
## Related docs
Required for custom session creation on the server.
Gate tracking until the user accepts.
Inspect runtime state and warnings.
# React Integration
Source: https://agentref.co/docs/integration/react
Install AgentRef in a React app without duplicate script injection or broken attribution in SPA navigation.
For plain React apps, the main goal is simple: load the AgentRef script once at the app shell and keep it available while the SPA navigates.
## Before you start
* Set the program website in AgentRef to the same root domain your React app uses.
* Copy the exact AgentRef script snippet from `Settings -> Integration`.
* If checkout happens on a sibling subdomain, install AgentRef there too.
## Recommended installation
For Vite, CRA, and most React SPAs, the cleanest setup is to add the script directly to the shared HTML shell.
```html theme={null}
```
If you do not control the HTML shell, inject the script once from the root component and guard against duplicates.
```tsx theme={null}
import { useEffect } from 'react';
export function AgentRefTracking() {
useEffect(() => {
if (document.querySelector('script[src*="/api/tracking/script.js"]')) {
return;
}
const script = document.createElement('script');
script.defer = true;
script.src = 'https://www.agentref.co/api/tracking/script.js?pid=YOUR_PROGRAM_ID';
document.head.appendChild(script);
}, []);
return null;
}
```
If you run React Strict Mode in development, an `index.html` install is usually less noisy than effect-based injection because it avoids duplicate development mounts.
## Checkout compatibility in React
| Checkout pattern | Recommendation |
| ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Stripe Payment Links, Buy Buttons, or Pricing Tables rendered by the SPA | Supported directly. AgentRef already observes late-rendered Stripe elements. |
| React app calls your own backend to create a Stripe Checkout Session | Supported, but you must forward `window.AgentRef.getCheckoutMetadata()` to your server. Use the [Stripe guide](/docs/integration/stripe). |
| Checkout jumps to a different root domain | First-party cookies do not cross root domains automatically. |
## SPA-specific notes
* AgentRef does not need to be reloaded on every route change.
* Hosted Stripe surfaces that render after navigation are still covered because the script watches the DOM.
* If you gate cookies behind consent, call `window.AgentRef.setConsent('granted')` after the visitor accepts.
## Verify the installation
1. Land on the app through a real affiliate link.
2. Confirm `agentref_cid` and `agentref_pid` in DevTools.
3. Add `?agentref_debug=1` and inspect the console.
4. Complete one supported checkout flow and confirm AgentRef records the conversion.
## Troubleshooting
* The script appears multiple times in the DOM: move the install into `index.html` or add a duplicate guard.
* Referral cookies are missing after a route transition: the problem is usually that the script never loaded on first render.
* Payment Links work but custom checkout does not: that means your React frontend is fine and your Stripe backend still needs the metadata bridge.
## Related docs
Bridge browser attribution into Stripe metadata.
Inspect runtime state and hosted Stripe instrumentation.
Coordinate AgentRef with your consent banner.
# Shopify Integration
Source: https://agentref.co/docs/integration/shopify
Install AgentRef on Shopify storefront pages and understand the current limits around Shopify's native checkout flow.
Shopify is the most important builder guide to read carefully because there are two separate questions: can AgentRef capture affiliate visits on the storefront, and can it automatically attribute Shopify-native orders? Those are not the same thing.
## Before you start
* Set the program website in AgentRef to the same root domain as your storefront.
* Copy the exact script snippet from `Settings -> Integration`.
* Decide whether your sales flow uses native Shopify checkout or a supported Stripe flow outside it.
## Install the script in your storefront theme
The most reliable Shopify install is still theme-level and global.
1. In Shopify admin, open `Online Store -> Themes`.
2. On your active theme, click `... -> Edit code`.
3. Open `layout/theme.liquid`.
4. Paste the AgentRef script before the closing `` tag.
```liquid theme={null}
```
5. Save and publish the theme change.
## What Shopify supports today
| Flow | Status with AgentRef |
| --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| Shopify storefront page with Stripe Payment Links, Buy Buttons, or Pricing Tables embedded directly on the page | Supported directly as long as the AgentRef script loads on that same page. |
| Custom app or backend that creates Stripe Checkout Sessions outside Shopify's native checkout | Supported, but you must follow the [Stripe guide](/docs/integration/stripe). |
| Native Shopify cart, checkout, or Shop Pay flow | Not an automatic AgentRef attribution surface today. |
This is the core product constraint: AgentRef does not automatically read native Shopify order events and turn them into attributed conversions just because the storefront has the tracking script installed.
## What that means in practice
* If Shopify is only your marketing layer and the actual paid checkout happens in your own Stripe flow, AgentRef can work well.
* If you rely on Shopify's own checkout, use a deliberate server-side reporting strategy or a different supported checkout surface.
* Do not promise affiliates automatic Shopify-native attribution unless you have built and tested that bridge intentionally.
## Multi-domain and consent notes
* If your storefront is `www.example.com` and your app or billing area is `app.example.com`, install AgentRef on both.
* If Shopify uses a different root domain than the page that starts checkout, cookies will not carry across automatically.
* If you use a consent banner, call `window.AgentRef.setConsent('granted')` after opt-in.
## Verify the installation
Open a real affiliate link to the storefront and confirm `agentref_cid` and `agentref_pid` appear.
View page source or DevTools Network on the live storefront.
If you use native Shopify checkout, stop here and document that this is not an automatic AgentRef conversion path. If you use Stripe outside Shopify checkout, continue with the [Stripe guide](/docs/integration/stripe).
## Troubleshooting
* The script is in the theme editor but not live: the edited theme may not be the active theme.
* Storefront clicks show up but conversions do not: this is expected for native Shopify checkout unless you built a separate attribution bridge.
* Cookies disappear between storefront and app: install AgentRef on both subdomains and keep everything under one root domain where possible.
## Related docs
Use this if your paid flow leaves native Shopify checkout and creates Stripe sessions directly.
Fallback when the platform-controlled checkout path cannot pass AgentRef through automatically.
Understand what the script does and what it does not do.
# Squarespace Integration
Source: https://agentref.co/docs/integration/squarespace
Install AgentRef with Squarespace code injection and understand where Squarespace-native checkout does or does not map to AgentRef automatically.
Squarespace is a good AgentRef fit for marketing pages and hosted Stripe surfaces. As with other website builders, the important distinction is whether Squarespace only renders the page or also owns the checkout.
## Before you start
* Set the program website in AgentRef to the same root domain as your Squarespace site.
* Copy the exact script snippet from `Settings -> Integration`.
* If checkout begins on another subdomain, install AgentRef there too.
## Install the script with code injection
1. Open your Squarespace site dashboard.
2. Go to `Settings -> Developer Tools -> Code Injection`.
3. Paste the AgentRef script into the `Header` field.
```html theme={null}
```
4. Save the change.
Squarespace code injection is typically available on Business plans and higher.
## Checkout compatibility on Squarespace
| Flow | Status with AgentRef |
| ---------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| Stripe Payment Links, Buy Buttons, or Pricing Tables embedded on Squarespace pages | Supported directly. |
| Custom Stripe checkout outside Squarespace Commerce | Supported with the [Stripe guide](/docs/integration/stripe). |
| Squarespace-native commerce checkout | Not an automatic AgentRef attribution surface today. |
## Multi-domain and consent notes
* Shared subdomains under one root can work well if AgentRef is installed on every surface that starts the journey.
* Different root domains do not share first-party cookies automatically.
* If you use a consent banner, call `window.AgentRef.setConsent('granted')` after opt-in.
## Verify the installation
Use a real affiliate link on the published Squarespace domain.
Check for `agentref_cid` and `agentref_pid` in DevTools.
Add `?agentref_debug=1` and inspect the console.
If you use anything beyond hosted Stripe surfaces, follow the [Stripe guide](/docs/integration/stripe).
## Troubleshooting
* The script is saved but cookies never appear: confirm the published domain matches the website configured in AgentRef.
* Clicks exist but conversions do not: Squarespace Commerce native checkout is not an automatic AgentRef conversion path.
* The flow crosses into another domain: first-party cookies will not move across root domains without a deliberate handoff.
## Related docs
Required for custom Squarespace-to-Stripe flows.
Understand the full click-to-conversion pipeline.
Coordinate AgentRef with your privacy banner.
# Stripe Checkout Integration
Source: https://agentref.co/docs/integration/stripe
Connect AgentRef to Stripe Payment Links, Buy Buttons, Pricing Tables, or custom Checkout Sessions.
This guide is the exact Stripe contract behind AgentRef's website integration. Use it whenever your site already has the AgentRef tracking script installed and you need to make sure conversions are attributed correctly.
## Routing decision first
Choose the route before changing code:
| Route | Use when | What to do |
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Route A — Hosted Stripe on the AgentRef website | A Stripe Payment Link, Buy Button, or Pricing Table is rendered on the website where AgentRef is installed. | Keep the flow intact and verify the AgentRef script is on that page. |
| Route B — Website custom checkout | The buy button is rendered on the website where AgentRef is installed, and that website calls your backend to create Stripe Checkout. | Read `window.AgentRef.getCheckoutMetadata()` on the website and pass it into Stripe metadata. |
| Route C — External app checkout | The buy button is rendered in a Chrome extension, desktop app, mobile app, external dashboard, or another surface where AgentRef is not installed. | Stop before implementation, explain the required checkout-route code change, ask for approval, then route checkout through a checkout start page on the AgentRef website before redirecting to Stripe. |
If Route C applies, it overrides Route B even when your backend creates the Stripe Checkout Session. Do not treat a metadata-only patch inside the external app as complete.
## First, identify your Stripe checkout type
| Checkout type | What AgentRef needs |
| ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Stripe Payment Links | Script on the page with the link. No backend bridge required. |
| Stripe Buy Buttons | Script on the page with the button. No backend bridge required. |
| Stripe Pricing Tables | Script on the page with the pricing table. No backend bridge required. |
| Server-created Stripe Checkout Session | Script plus a metadata bridge from browser to server to Stripe. |
| Direct PaymentIntents / custom Stripe flow | Script plus a metadata bridge into your payment intent or checkout backend. |
| External app, Chrome extension, desktop app, or mobile app checkout | Route the buy button through a checkout start page on the website where the AgentRef script is installed, then redirect to Stripe. |
AgentRef already instruments hosted Stripe surfaces by itself. If the page contains a real buy.stripe.com link, \, or \, you do not need an extra helper script.
If a flow is both server-created and started from a Chrome extension, desktop app, mobile app, or external dashboard, treat it as an external app checkout. A metadata-only patch inside the external app is not enough unless that app can actually read the AgentRef cookies from the website where the script is installed.
## Hosted Stripe surfaces
If you use Stripe Payment Links, Buy Buttons, or Pricing Tables, install the AgentRef script on the same page as the Stripe element and keep the rest of your flow intact.
### What AgentRef does automatically
* Appends `client_reference_id=` to `buy.stripe.com` links
* Sets `client-reference-id` on `stripe-buy-button`
* Sets `client-reference-id` on `stripe-pricing-table`
* Watches for late-rendered Stripe elements with a `MutationObserver`
That means hosted Stripe surfaces work well for static sites, SPAs, and builders as long as the AgentRef script loads on the page where the visitor clicks.
## Custom Checkout Sessions
If your CTA goes to your own route, API, server action, or backend first, the script alone is not enough. You need one small bridge: read the AgentRef checkout metadata in the browser and forward it into Stripe when your server creates the session.
### Step 1: Read AgentRef metadata in the browser
```js theme={null}
window.AgentRef.ready(async function () {
const agentrefMeta = window.AgentRef.getCheckoutMetadata();
const response = await fetch('/api/create-checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
priceId: 'price_123',
agentrefMeta,
}),
});
const { url } = await response.json();
window.location.href = url;
});
```
### Step 2: Pass the metadata into Stripe on the server
```ts Node.js theme={null}
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: priceId, quantity: 1 }],
success_url: 'https://example.com/success',
cancel_url: 'https://example.com/pricing',
metadata: {
...(agentrefMeta || {}),
},
subscription_data: {
metadata: {
...(agentrefMeta || {}),
},
},
});
```
```ts One-time payment theme={null}
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: [{ price: priceId, quantity: 1 }],
success_url: 'https://example.com/success',
cancel_url: 'https://example.com/pricing',
metadata: {
...(agentrefMeta || {}),
},
payment_intent_data: {
metadata: {
...(agentrefMeta || {}),
},
},
});
```
## External apps and Chrome extensions
If the buy button does not live on the website where the AgentRef script is installed, do not send the user directly from that external surface to Stripe. This includes Chrome extensions, desktop apps, mobile apps, external dashboards, and any app surface running on a different origin.
Do not treat a metadata-only patch inside the external app as complete. The AgentRef cookie usually lives on your website, not inside `chrome-extension://...` or another external origin.
Instead, route the button to a checkout start page on your website:
```txt theme={null}
Chrome extension or external app
-> https://your-domain.com/checkout/start?plan=pro
-> your website reads AgentRef attribution
-> your backend creates the Stripe Checkout Session with AgentRef metadata
-> redirect to Stripe Checkout
```
The checkout start page should run on the same root domain as your AgentRef script. In the simplest implementation, that page loads your normal app shell, calls `window.AgentRef.getCheckoutMetadata()`, sends the metadata to your existing checkout backend, and then redirects to the Stripe Checkout URL.
If an AI coding agent detects this external-app pattern, instruct it to stop before implementation, explain that checkout must route through the website first, and ask for approval before editing code. This is a code-change approval gate for the checkout-route rewrite, not a deployment approval gate. It avoids silent checkout-flow rewrites and incomplete metadata-only patches.
## Exact metadata keys
AgentRef expects the metadata object to pass through unchanged:
```json theme={null}
{
"agentref_cid": "clk_abc123",
"agentref_pid": "550e8400-e29b-41d4-a716-446655440000",
"agentref_source": "script_direct"
}
```
Do not rename these keys, flatten them differently, or trim them down to only one field. AgentRef's Stripe webhook reads this contract exactly.
## What not to do
* Do not add a second attribution or reporting pipeline for the same Stripe payment.
* Do not overwrite your own `client_reference_id` on custom Checkout Sessions unless that field is already reserved for AgentRef.
* Do not send external app or Chrome extension buy buttons directly to Stripe when the AgentRef script only runs on your website.
* Do not treat an external app request-schema or Stripe-metadata patch as complete if the flow still starts checkout outside the AgentRef website.
* Do not let an AI coding agent silently reroute checkout without your approval.
* Do not assume Shopify, Webflow Ecommerce, WooCommerce, Wix Stores, or other platform-native checkouts will magically pass this metadata into Stripe. If the platform owns checkout and does not expose Stripe session creation, treat that as a separate integration problem.
## Verify the Stripe path
Open a real affiliate link and make sure agentref\_cid and agentref\_pid are present before checkout starts.
Complete a test purchase through the exact flow you use in production.
In Stripe, confirm that the session metadata includes agentref\_cid, agentref\_pid, and agentref\_source, or that the hosted surface received a client\_reference\_id.
In AgentRef, confirm the conversion appears under Conversions and the tracking diagnostics no longer show a Stripe-event blocker.
## Troubleshooting
| Problem | What to check |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Hosted Stripe link was clicked but no conversion appeared | Make sure the AgentRef script was loaded on the same page as the link or Stripe element. |
| Custom session created but conversion missing | Check that your backend wrote the AgentRef keys into `metadata` and `subscription_data.metadata` or `payment_intent_data.metadata`. |
| Chrome extension or external app checkout is missing attribution | Route the buy button through a checkout start page on the website where the AgentRef script is installed, then create Stripe Checkout from there. |
| Pricing table or buy button renders late | AgentRef already watches the DOM for late Stripe elements. Usually this means the script was not installed at all or loaded on the wrong page. |
| Tracking health still shows a Stripe blocker | You may have clicks but no completed Stripe event yet. Finish one supported checkout flow end-to-end. |
## Related docs
Core script behavior and `window.AgentRef`.
Full click-to-commission pipeline.
Inspect hosted Stripe instrumentation and runtime warnings.
# Svelte and SvelteKit Integration
Source: https://agentref.co/docs/integration/svelte
Install AgentRef in SvelteKit or plain Svelte and keep attribution stable across navigation.
Svelte and SvelteKit both work well with AgentRef as long as the script is installed in the global shell instead of a page-specific component.
## Before you start
* Set the program website in AgentRef to the same root domain your app serves.
* Copy the exact script snippet from `Settings -> Integration`.
* If your app and checkout start on different subdomains, install AgentRef on both.
## Recommended installation
Install the script in `src/app.html` so it is available across all routes.
```html theme={null}
%sveltekit.head%
%sveltekit.body%
```
For plain Svelte apps, install the script in the shared HTML shell.
```html theme={null}
```
## Checkout compatibility in Svelte
| Checkout pattern | Recommendation |
| ----------------------------------------------- | ------------------------------------------------------------ |
| Hosted Stripe surfaces on Svelte pages | Supported directly. |
| Custom Checkout Sessions created by your server | Supported with the [Stripe guide](/docs/integration/stripe). |
| Checkout on a different root domain | Not automatic because AgentRef uses first-party cookies. |
## Svelte-specific notes
* Do not reinstall AgentRef in `onMount()` for every route. One shell-level install is enough.
* Hosted Stripe elements rendered after hydration are still observed automatically.
* If you gate cookies behind consent, call `window.AgentRef.setConsent('granted')` after opt-in.
## Verify the installation
1. Open the live app through a real affiliate link.
2. Confirm `agentref_cid` and `agentref_pid` in browser cookies.
3. Add `?agentref_debug=1` and inspect the console.
4. Complete one supported checkout flow and verify the conversion in AgentRef.
## Troubleshooting
* `window.AgentRef` is undefined: the script was not installed in the global shell.
* Clicks appear but conversions do not: your custom checkout path still needs the [Stripe metadata bridge](/docs/integration/stripe).
* Cookies fail across domains: move the journey onto one root or handle the handoff server-side.
## Related docs
Metadata bridge for custom Stripe sessions.
Coordinate AgentRef with your consent banner.
Runtime inspection and troubleshooting.
# Vue.js Integration
Source: https://agentref.co/docs/integration/vuedotjs
Install AgentRef in a Vue app and keep attribution stable across client-side navigation.
Vue works best with AgentRef when the script is installed once in the app shell instead of being recreated on every page.
## Before you start
* Set the program website in AgentRef to the same root domain your Vue app serves.
* Copy the exact script snippet from `Settings -> Integration`.
* If your marketing site and checkout app live on different subdomains, install on both when both can start the conversion path.
## Recommended installation
For Vue apps built with Vite, install the script in the shared HTML shell.
```html theme={null}
```
If you need a runtime install, append the script before mounting the app.
```ts theme={null}
import { createApp } from 'vue';
import App from './App.vue';
if (!document.querySelector('script[src*="/api/tracking/script.js"]')) {
const script = document.createElement('script');
script.defer = true;
script.src = 'https://www.agentref.co/api/tracking/script.js?pid=YOUR_PROGRAM_ID';
document.head.appendChild(script);
}
createApp(App).mount('#app');
```
## Checkout compatibility in Vue
| Checkout pattern | Recommendation |
| --------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| Stripe Payment Links, Buy Buttons, or Pricing Tables | Supported directly. Keep the script on the page that renders them. |
| Vue frontend calls an API that creates Stripe Checkout Sessions | Supported, but you must pass `window.AgentRef.getCheckoutMetadata()` to that API first. |
| Separate root-domain checkout | Not automatic. Cookies are first-party and stay on one root. |
## Notes for Vue SPAs
* AgentRef does not need to be re-mounted on route changes.
* Late-rendered Stripe elements are still instrumented automatically.
* If you use a cookie banner component, call `window.AgentRef.setConsent('granted')` after the visitor accepts.
## Verify the installation
Open a live affiliate link that lands on your Vue app.
Confirm `agentref_cid` and `agentref_pid`, then add `?agentref_debug=1`.
Use the AgentRef dashboard, tracking status endpoint, or MCP `verify_tracking` tool.
## Troubleshooting
* `window.AgentRef` never appears: install the script earlier, ideally in `index.html`.
* Conversions are missing while clicks arrive: your Stripe session creation path still needs the [Stripe guide](/docs/integration/stripe).
* Cookies disappear between domains: use one shared root domain or install deliberately on every participating subdomain.
## Related docs
Metadata handoff for custom checkout sessions.
Cookie, parameter, and DOM API reference.
Runtime inspection and warnings.
# Webflow Integration
Source: https://agentref.co/docs/integration/webflow
Install AgentRef on a Webflow site and understand when Webflow pages are a good fit for AgentRef's supported checkout contract.
Webflow is a strong fit for AgentRef when it is used for a marketing site, landing pages, or pages that host Stripe links and embeds.
## Before you start
* Set the program website in AgentRef to the same root domain your Webflow site uses.
* Copy the exact script snippet from `Settings -> Integration`.
* If your app or checkout starts on another subdomain, install AgentRef there too.
## Install the script site-wide
1. Open your Webflow project.
2. Go to `Site Settings -> Custom Code`.
3. Paste the AgentRef script into the `Head Code` field.
```html theme={null}
```
4. Save the change.
5. Publish the site.
Webflow custom code only runs on the published site, not in the Designer preview.
## Checkout compatibility on Webflow
| Flow | Status with AgentRef |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------ |
| Stripe Payment Links, Buy Buttons, or Pricing Tables embedded on Webflow pages | Supported directly. |
| Custom Stripe checkout you own outside Webflow Ecommerce | Supported with the [Stripe guide](/docs/integration/stripe). |
| Webflow Ecommerce native checkout | Not an automatic AgentRef attribution surface today. |
## Multi-domain and consent notes
* Webflow marketing site plus app subdomain is fine if both live under one root and both install AgentRef where needed.
* Different root domains do not share first-party cookies automatically.
* If you gate cookies behind consent, call `window.AgentRef.setConsent('granted')` after opt-in.
## Verify the installation
1. Publish the site.
2. Open a real affiliate link to the live Webflow domain.
3. Confirm `agentref_cid` and `agentref_pid` exist in browser cookies.
4. Add `?agentref_debug=1` and inspect the console.
5. If you use custom Stripe checkout, validate the [Stripe guide](/docs/integration/stripe) end-to-end.
## Troubleshooting
* The script works in code settings but not live: republish the site after saving.
* The script exists but clicks are not verified: confirm the program website in AgentRef matches the Webflow domain.
* Conversions are missing while clicks arrive: you are either using Webflow Ecommerce native checkout or your custom Stripe path still needs the metadata bridge.
## Related docs
Required for custom Webflow-to-Stripe checkout paths.
Inspect hosted Stripe instrumentation and runtime warnings.
Coordinate AgentRef with your consent manager.
# Wix Integration
Source: https://agentref.co/docs/integration/wix
Install AgentRef on a Wix site and understand the limits of Wix-native ecommerce checkout with AgentRef.
Wix works well for AgentRef as a marketing site or a page that hosts supported Stripe elements. Like several builders, it becomes a different conversation once Wix owns the actual checkout path.
## Before you start
* Set the program website in AgentRef to the same root domain your Wix site uses.
* Copy the exact script snippet from `Settings -> Integration`.
* If your app or checkout starts on another subdomain, install AgentRef there too.
## Install the script through Wix custom code
1. Open the Wix Editor for your site.
2. Go to `Settings -> Custom Code`.
3. Click `Add Custom Code`.
4. Choose `All Pages`.
5. Choose placement in `Head`.
6. Paste the AgentRef script:
```html theme={null}
```
7. Apply the change and publish the site.
Wix custom code runs on the published site, not in editor preview.
## Checkout compatibility on Wix
| Flow | Status with AgentRef |
| ----------------------------------------------------------------- | ------------------------------------------------------------ |
| Stripe Payment Links, Buy Buttons, or Pricing Tables on Wix pages | Supported directly. |
| Custom Stripe checkout outside Wix Stores | Supported with the [Stripe guide](/docs/integration/stripe). |
| Wix Stores or another Wix-native ecommerce checkout | Not an automatic AgentRef attribution surface today. |
## Multi-domain and consent notes
* Shared subdomains under one root work best when AgentRef is installed on both sides of the journey.
* Different root domains cannot share first-party cookies automatically.
* If you use a cookie banner, call `window.AgentRef.setConsent('granted')` after opt-in.
## Verify the installation
1. Publish the site.
2. Visit the live Wix domain through a real affiliate link.
3. Confirm `agentref_cid` and `agentref_pid` in browser cookies.
4. Add `?agentref_debug=1` and inspect the console.
5. If you use custom Stripe checkout, verify the [Stripe guide](/docs/integration/stripe) path too.
## Troubleshooting
* The code is saved in Wix but not live: publish the site again after the change.
* Clicks appear but no conversion does: you are likely in a Wix-native checkout path or missing the Stripe metadata bridge.
* Cookies disappear between marketing site and app: install AgentRef on both subdomains and keep them on one root domain.
## Related docs
Required for custom Wix-to-Stripe flows.
Fallback when the platform-owned checkout cannot pass AgentRef automatically.
Coordinate AgentRef with your privacy banner.
# WordPress Integration
Source: https://agentref.co/docs/integration/wordpress
Install AgentRef on a WordPress site and understand when WordPress checkout flows do or do not map cleanly to AgentRef.
WordPress gives you multiple installation options, but the safest AgentRef setup is still the same: load the script site-wide and keep checkout on a supported Stripe path.
## Before you start
* Set the program website in AgentRef to the same root domain your WordPress site uses.
* Copy the exact script snippet from `Settings -> Integration`.
* If WordPress is only your marketing site and checkout happens on `app.example.com`, install AgentRef on both surfaces.
## Recommended installation
If your site already uses a site-wide header-code plugin or theme option, paste the AgentRef script into the global head field.
```html theme={null}
```
If you prefer code, add the script through `wp_head` in your child theme instead of editing the parent theme directly.
```php theme={null}
add_action('wp_head', function () {
?>
If you use caching, minification, or CDN plugins, clear those caches after installing the script.
## Checkout compatibility on WordPress
| Checkout pattern | Recommendation |
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Stripe Payment Links, Buy Buttons, or Pricing Tables embedded on WordPress pages | Supported directly. |
| Custom Stripe Checkout Session created by your own WordPress plugin or backend code | Supported, but you must follow the [Stripe guide](/docs/integration/stripe). |
| WooCommerce or another native WordPress ecommerce checkout | Not an automatic AgentRef checkout surface today unless you explicitly bridge attribution into Stripe metadata or report conversions server-side. |
This is the key WordPress caveat: installing the script on a WooCommerce storefront captures referral visits, but it does not by itself guarantee automatic order attribution through WooCommerce's native checkout path.
## Multi-domain and consent notes
* Shared subdomains under one root can share AgentRef cookies if the script is installed where the journey begins.
* Different root domains cannot share first-party cookies automatically.
* If you use a consent manager, call `window.AgentRef.setConsent('granted')` after the visitor opts in.
## Verify the installation
Land on your published WordPress site with a real referral link.
Check for `agentref_cid` and `agentref_pid` in browser cookies.
Add `?agentref_debug=1` or `?agentref_verify=1` to a live page and inspect the result.
If you use anything beyond hosted Stripe surfaces, confirm your checkout integration matches the [Stripe guide](/docs/integration/stripe).
## Troubleshooting
* The script is visible in WordPress admin but not on the live site: a cache or optimization layer is still serving the old template.
* Clicks arrive but sales do not: this usually means WooCommerce or another plugin owns checkout and does not pass AgentRef metadata into Stripe.
* Cookies exist on the marketing site but not in the app: install AgentRef on both subdomains.
## Related docs
Required for custom WordPress-to-Stripe checkout bridges.
Use this when your commerce stack cannot pass through AgentRef automatically.
Coordinate AgentRef with your cookie banner.
# Connect Stripe
Source: https://agentref.co/docs/merchant-guide/connect-stripe
Link your Stripe account to AgentRef so affiliate conversions are tracked automatically from payment events.
AgentRef uses Stripe Connect (OAuth) to listen for payment events on your account. When a customer pays, AgentRef matches the payment to the affiliate who referred them and creates a conversion automatically.
No payment processing access is required. AgentRef only reads payment events.
## What happens during connection
When you click **Connect Stripe** in the dashboard (or call the API), the flow works like this:
```mermaid theme={null}
sequenceDiagram
participant M as Merchant
participant A as AgentRef
participant S as Stripe
M->>A: Click "Connect Stripe"
A->>M: Redirect to Stripe OAuth page
M->>S: Authorize AgentRef
S->>A: OAuth callback with auth code
A->>S: Exchange code for access token
A->>A: Store connection, register webhooks
A->>M: Redirect back to dashboard (connected)
```
From the dashboard, go to **Program Settings > Stripe** and click **Connect Stripe**. You'll be redirected to Stripe's authorization page.
Alternatively, use the API to get an OAuth URL:
```bash cURL theme={null}
curl -X POST https://www.agentref.co/api/v1/programs/PROGRAM_ID/connect-stripe \
-H "Authorization: Bearer ak_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"method": "oauth_url"}'
```
```javascript Node.js theme={null}
import { AgentRef } from 'agentref';
const client = new AgentRef({ apiKey: 'ak_live_YOUR_KEY' });
const result = await client.programs.connectStripe('PROGRAM_ID', {
method: 'oauth_url',
});
console.log(result.authUrl); // Redirect the merchant to this URL
```
```python Python theme={null}
from agentref import AgentRef
client = AgentRef(api_key="ak_live_YOUR_KEY")
result = client.programs.connect_stripe("PROGRAM_ID", method="oauth_url")
print(result.auth_url) # Redirect the merchant to this URL
```
The response includes an `authUrl` that you open in the browser to start the Stripe OAuth flow.
On the Stripe authorization page, review the permissions AgentRef is requesting and click **Connect**. If you have multiple Stripe accounts, select the one you want to use for this program.
After authorization, AgentRef automatically registers webhooks on your Stripe account. You do not need to configure anything manually in Stripe's dashboard.
## Permissions AgentRef receives
AgentRef requests **read-only** access to payment events. Here is exactly what it can and cannot do:
| Can do | Cannot do |
| -------------------------------------- | ----------------------------------- |
| Listen for payment events via webhooks | Process payments on your behalf |
| Read checkout session metadata | Access your bank account or balance |
| Read invoice and subscription data | Create charges or transfers |
| Read refund events | Modify your Stripe settings |
AgentRef uses Stripe Connect in **read-only mode**. It never touches your funds or payment processing. You can revoke access at any time from your Stripe dashboard under **Settings > Connected accounts**.
## Webhook events AgentRef listens to
Once connected, AgentRef automatically subscribes to these Stripe webhook events:
| Event | Purpose |
| ------------------------------- | --------------------------------------------------------------- |
| `checkout.session.completed` | Captures one-time purchases and new subscriptions from Checkout |
| `invoice.paid` | Tracks recurring subscription payments |
| `charge.succeeded` | Captures direct charges (Payment Intents, legacy charges) |
| `charge.refunded` | Reverses commissions when a refund is issued |
| `customer.subscription.deleted` | Stops recurring commissions when a subscription is canceled |
These webhooks are managed by AgentRef. You do not need to create or maintain them in Stripe's webhook settings.
If you already have a webhook endpoint in Stripe pointing to AgentRef from a previous connection, the reconnect flow will reuse it. AgentRef does not create duplicate endpoints.
## Disconnect Stripe
If you need to disconnect Stripe from a program:
```bash cURL theme={null}
curl -X DELETE https://www.agentref.co/api/v1/programs/PROGRAM_ID/connect-stripe \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
```javascript Node.js theme={null}
const result = await client.programs.disconnectStripe('PROGRAM_ID');
console.log(result.success); // true
```
```python Python theme={null}
result = client.programs.disconnect_stripe("PROGRAM_ID")
print(result.success) # True
```
Disconnecting removes the webhook registration and stops conversion tracking for that program. Existing conversions and commissions are not affected.
## Troubleshooting
| Problem | Solution |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| OAuth flow fails or times out | Check that third-party cookies are enabled in your browser. Some ad blockers interfere with the OAuth redirect. |
| Webhooks not firing | Verify the connection is active in **Program Settings > Stripe**. If needed, disconnect and reconnect. |
| Conversions not appearing | Check that your tracking script passes `agentref_cid` metadata to Stripe. See [Install Tracking](/docs/merchant-guide/install-tracking). |
| Multiple Stripe accounts | Each program connects to one Stripe account. Use separate programs for separate Stripe accounts. |
## What's next
Configure your commission rates, cookie duration, and program settings.
Add the tracking script to your website to capture affiliate clicks.
# Conversions
Source: https://agentref.co/docs/merchant-guide/conversions
Understand the conversion lifecycle, how Stripe events create conversions, and how to query and aggregate your conversion data.
A conversion is created every time an affiliate-referred customer completes a purchase. It records the sale amount, the commission owed, and the current status of that commission. Conversions are the backbone of your affiliate program – everything from payouts to fraud detection is tied to them.
## Conversion lifecycle
```
pending → approved → paid
↓ ↓
rejected partially_refunded → refunded
```
| Status | What it means |
| -------------------- | ------------------------------------------------------------------------------ |
| `pending` | Conversion created from a Stripe event. Waiting for review or approval window. |
| `approved` | Commission confirmed. Will be included in the next payout. |
| `partially_refunded` | Part of the Stripe charge was refunded. Commission is reduced proportionally. |
| `refunded` | The underlying Stripe charge was fully refunded. Commission is voided. |
| `rejected` | Manually rejected or flagged as fraudulent. Commission is voided. |
Most programs approve conversions automatically. If your program has fraud detection enabled, suspicious conversions may stay in `pending` until you resolve the associated flag.
## How conversions are created
AgentRef listens to your connected Stripe account for `checkout.session.completed` and `invoice.payment_succeeded` events. When a purchase is traced back to an affiliate click (via cookie or referral code), a conversion is created automatically – no server-side code needed beyond the tracking snippet.
The conversion captures:
* `amount` – the charged amount in cents
* `currency` – from the Stripe charge
* `customerEmail` – from the Stripe customer
* `commission` – calculated from your program's `commissionPercent`
* `commissionType` – `one_time` or `recurring` / `recurring_limited`
## Viewing conversions in the dashboard
Go to **Programs > \[Your Program] > Conversions**. You can filter by status, date range, or affiliate. Click any row to see the full conversion detail including the originating click and Stripe charge ID.
## API reference
### List conversions
Scope: `conversions:read`
```bash cURL theme={null}
curl "https://www.agentref.co/api/v1/conversions?status=pending&limit=25" \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
```javascript Node.js theme={null}
import { AgentRef } from 'agentref';
const client = new AgentRef({ apiKey: 'ak_live_YOUR_KEY' });
const { data, meta } = await client.conversions.list({
status: 'pending',
limit: 25,
});
for (const conversion of data) {
console.log(conversion.id, conversion.amount, conversion.status);
}
```
```python Python theme={null}
from agentref import AgentRef
client = AgentRef(api_key='ak_live_YOUR_KEY')
result = client.conversions.list(status='pending', limit=25)
for conversion in result.data:
print(conversion.id, conversion.sale_amount, conversion.status)
```
**Query parameters**
| Parameter | Type | Description |
| -------------------- | ------------ | --------------------------------------------------------------------------- |
| `programId` | UUID | Filter to a specific program |
| `affiliateId` | UUID | Filter to a specific affiliate |
| `status` | string | `pending` \| `approved` \| `partially_refunded` \| `rejected` \| `refunded` |
| `startDate` / `from` | ISO datetime | Start of date range |
| `endDate` / `to` | ISO datetime | End of date range |
| `limit` | number | Max results (1–100) |
| `page` / `offset` | number | Pagination |
### Get conversion stats
Returns aggregate metrics for a time period. Scope: `stats:read`
```bash cURL theme={null}
curl "https://www.agentref.co/api/v1/conversions/stats?period=30d&programId=PROGRAM_ID" \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
```javascript Node.js theme={null}
const stats = await client.conversions.stats({
period: '30d',
programId: 'PROGRAM_ID',
});
console.log(stats.total);
console.log(stats.totalRevenue); // in cents
console.log(stats.totalCommissions);
```
```python Python theme={null}
stats = client.conversions.stats(period='30d', program_id='PROGRAM_ID')
print(stats.total)
print(stats.total_revenue) # in cents
print(stats.total_commissions)
```
**Period options:** `7d`, `30d`, `90d`, `all`
### Get recent conversions
Returns the most recent conversions (up to 20). Useful for dashboard widgets and webhooks verification. Scope: `conversions:read`
```bash cURL theme={null}
curl "https://www.agentref.co/api/v1/conversions/recent?limit=5" \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
```javascript Node.js theme={null}
const recent = await client.conversions.recent({ limit: 5 });
```
```python Python theme={null}
recent = client.conversions.recent(limit=5)
```
## What's next
Learn how approved conversions become payouts to your affiliates.
See how AgentRef flags suspicious conversions before they're approved.
# Create Program
Source: https://agentref.co/docs/merchant-guide/create-program
Set up your affiliate program with commission rates, cookie duration, payout rules, and approval settings.
A program is the core unit in AgentRef. It defines how affiliates earn commissions, how long attribution cookies last, and when payouts happen. You can run multiple programs under one merchant account.
## Create a program
After connecting Stripe, the onboarding flow walks you through creating your first program. You can also create additional programs from **Programs > New Program** at any time.
```bash cURL theme={null}
curl -X POST https://www.agentref.co/api/v1/programs \
-H "Authorization: Bearer ak_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "My SaaS Affiliate Program",
"commissionType": "recurring",
"commissionPercent": 25,
"cookieDuration": 30,
"payoutThreshold": 5000,
"payoutFrequency": "monthly",
"autoApproveAffiliates": true,
"currency": "usd"
}'
```
```javascript Node.js theme={null}
import { AgentRef } from 'agentref';
const client = new AgentRef({ apiKey: 'ak_live_YOUR_KEY' });
const program = await client.programs.create({
name: 'My SaaS Affiliate Program',
commissionType: 'recurring',
commissionPercent: 25,
cookieDuration: 30,
payoutThreshold: 5000,
payoutFrequency: 'monthly',
autoApproveAffiliates: true,
currency: 'usd',
});
console.log(program.id);
```
```python Python theme={null}
from agentref import AgentRef
client = AgentRef(api_key="ak_live_YOUR_KEY")
program = client.programs.create(
name="My SaaS Affiliate Program",
commission_type="recurring",
commission_percent=25,
cookie_duration=30,
payout_threshold=5000,
payout_frequency="monthly",
auto_approve_affiliates=True,
currency="usd",
)
print(program.id)
```
## Program settings
### Required settings
| Setting | Type | Description |
| ------------------- | ------ | ---------------------------------------------------------------------------------------------- |
| `name` | string | Display name for your program. Shown to affiliates and in the marketplace. Max 100 characters. |
| `commissionType` | string | How commissions are calculated. See commission types below. |
| `commissionPercent` | number | Percentage of each sale the affiliate earns. Range: 1–100. |
### Optional settings
| Setting | Default | Description |
| ----------------------- | --------- | ------------------------------------------------------------------------------------------------------- |
| `description` | `null` | Program description (max 500 characters). Shown in marketplace listings. |
| `landingPageUrl` | `null` | Default URL where affiliate links redirect visitors. |
| `cookieDuration` | `30` | Days the attribution cookie persists. Range: 1–365. |
| `payoutThreshold` | `5000` | Minimum commission balance (in cents) before an affiliate is eligible for payout. Range: 1,000–100,000. |
| `payoutFrequency` | `monthly` | How often payouts are processed: `weekly`, `monthly`, or `quarterly`. |
| `autoApproveAffiliates` | `false` | Whether new affiliates are approved automatically or require manual review. |
| `commissionLimitMonths` | `null` | For `recurring_limited` type only. Number of months commissions are paid. Range: 1–36. |
| `portalSlug` | `null` | Custom slug for your affiliate portal URL. Lowercase alphanumeric and hyphens only. |
| `currency` | `usd` | Currency code for commissions and payouts. |
The `payoutThreshold` is specified in **cents**. A value of `5000` means \$50.00.
## Commission types
AgentRef supports three commission models:
### One-time (`one_time`)
The affiliate earns a commission on the first payment only. If a customer upgrades, renews, or makes additional purchases, no further commissions are generated.
Best for: One-time purchases, lifetime deals, products without subscriptions.
### Recurring (`recurring`)
The affiliate earns a commission on every payment the referred customer makes, for the lifetime of the subscription. If the customer pays monthly, the affiliate earns every month.
Best for: SaaS with subscriptions. This is the most popular model because it incentivizes affiliates to refer customers who stick around.
### Recurring with limit (`recurring_limited`)
Same as recurring, but commissions stop after a configured number of months (`commissionLimitMonths`). For example, 25% for the first 12 months.
Best for: SaaS that wants to cap lifetime affiliate payouts while still rewarding ongoing referrals during the customer's early months.
| Type | First payment | Subsequent payments | Cap |
| ------------------- | ----------------- | ------------------- | -------------------- |
| `one_time` | Commission earned | No commission | N/A |
| `recurring` | Commission earned | Commission earned | None |
| `recurring_limited` | Commission earned | Commission earned | Stops after N months |
## Program status lifecycle
Every program has a `status` that controls whether it is actively tracking conversions:
```mermaid theme={null}
graph LR
A[active] -->|Pause| B[paused]
B -->|Resume| A
A -->|Archive| C[archived]
B -->|Archive| C
```
| Status | Behavior |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `active` | Tracking is live. New clicks, conversions, and commissions are processed normally. |
| `paused` | Tracking stops. Existing data is preserved but no new conversions are created. Affiliate links still resolve but clicks are not recorded. |
| `archived` | Program is soft-deleted. It no longer appears in listings or API responses (unless explicitly filtered). Existing conversions and payouts are preserved for historical records. |
Update a program's status via the API:
```bash cURL theme={null}
curl -X PATCH https://www.agentref.co/api/v1/programs/PROGRAM_ID \
-H "Authorization: Bearer ak_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "paused"}'
```
```javascript Node.js theme={null}
await client.programs.update('PROGRAM_ID', { status: 'paused' });
```
```python Python theme={null}
client.programs.update("PROGRAM_ID", status="paused")
```
## Program readiness
AgentRef tracks whether your program is fully configured and ready to process conversions. The `readiness` field on a program can be:
| Readiness | Meaning |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `setup` | Initial state. Stripe is not connected and tracking is not installed. |
| `partial` | Some steps are complete but the program is not fully operational. For example, Stripe is connected but the tracking script has not been verified. |
| `ready` | Stripe is connected, tracking script is verified, and the program can process conversions end-to-end. |
Check readiness via the API by fetching the program detail:
```bash cURL theme={null}
curl https://www.agentref.co/api/v1/programs/PROGRAM_ID \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
```javascript Node.js theme={null}
const program = await client.programs.get('PROGRAM_ID');
console.log(program.readiness); // "setup" | "partial" | "ready"
```
```python Python theme={null}
program = client.programs.get("PROGRAM_ID")
print(program.readiness) # "setup" | "partial" | "ready"
```
## Recommended starting settings
If you are unsure where to start, these are industry-standard defaults for SaaS affiliate programs:
| Setting | Recommendation | Why |
| -------------------- | -------------- | ---------------------------------------------------------------------------- |
| **Commission type** | `recurring` | Incentivizes affiliates to refer customers who stick around. |
| **Commission rate** | 20–30% | Competitive enough to attract affiliates. Industry standard for SaaS. |
| **Cookie duration** | 30 days | Balances fair attribution with a reasonable conversion window. |
| **Payout threshold** | \$50 | Low enough to keep affiliates motivated, high enough to avoid micro-payouts. |
| **Payout frequency** | Monthly | Standard cadence. Weekly works for high-volume programs. |
| **Auto-approve** | Yes | Reduces friction. You can always block bad actors later. |
You can change all of these settings later. Start simple and adjust based on real performance data.
## What's next
Link your Stripe account if you haven't already.
Add the tracking script to your website to start capturing clicks.
# Fraud Detection
Source: https://agentref.co/docs/merchant-guide/fraud-detection
How AgentRef detects and flags suspicious affiliate activity automatically.
AgentRef automatically monitors affiliate activity and flags suspicious patterns. You decide what to do with each flag.
## How It Works
AgentRef analyzes clicks, conversions, and affiliate behavior in real-time. When something looks suspicious, a **fraud flag** is created for your review.
### Flag Types
| Flag Type | What It Detects |
| -------------------------- | ------------------------------------------------------ |
| Suspicious click patterns | Unusual click volume or timing from a single affiliate |
| Self-referrals | Affiliate referring their own purchases |
| Geographic anomalies | Clicks from unexpected regions |
| Conversion velocity | Too many conversions in a short time window |
| Cookie stuffing indicators | Signs of forced cookie injection |
### Trust Tiers
Affiliates are assigned trust tiers based on their history:
| Tier | Behavior |
| ------------ | ------------------------------------------------ |
| **New** | All conversions require manual review |
| **Trusted** | Conversions auto-approved, flags still generated |
| **Verified** | Full auto-approval, reduced monitoring |
Affiliates move between tiers automatically based on their track record. You can also manually adjust tiers.
## Viewing Flags
### In the Dashboard
Go to your program dashboard → **Fraud Flags** to see all open flags with details about why they were raised.
### Via API
```javascript Node.js theme={null}
// List open flags
const flags = await client.flags.list({
programId: 'prg_abc123',
status: 'open',
})
// Get flag statistics
const stats = await client.flags.stats({
programId: 'prg_abc123',
})
```
```python Python theme={null}
flags = client.flags.list(
program_id="prg_abc123",
status="open",
)
stats = client.flags.stats(program_id="prg_abc123")
```
```bash cURL theme={null}
# List flags
curl "https://www.agentref.co/api/v1/flags?programId=prg_abc123&status=open" \
-H "Authorization: Bearer ak_live_YOUR_KEY"
# Flag stats
curl "https://www.agentref.co/api/v1/flags/stats?programId=prg_abc123" \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
## Resolving Flags
Review each flag and decide whether to dismiss it or take action:
```javascript Node.js theme={null}
// Dismiss a flag (false positive)
await client.flags.resolve('flag_abc123', {
resolution: 'dismissed',
})
// Confirm fraud and block the affiliate
await client.flags.resolve('flag_abc123', {
resolution: 'confirmed',
})
```
```bash cURL theme={null}
curl -X POST "https://www.agentref.co/api/v1/flags/flag_abc123/resolve" \
-H "Authorization: Bearer ak_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"resolution": "dismissed"}'
```
### Resolution Options
| Resolution | Effect |
| ----------- | ---------------------------------------------------------------- |
| `dismissed` | Flag closed, no action taken (false positive) |
| `confirmed` | Flag confirmed as fraud – associated conversions can be reversed |
## Webhook Integration
Subscribe to `flag.resolved` webhook events to keep your systems in sync:
```json theme={null}
{
"type": "flag.resolved",
"data": {
"id": "flag_abc123",
"affiliateId": "aff_def456",
"type": "suspicious_click_pattern",
"resolution": "dismissed",
"resolvedAt": "2026-03-23T14:00:00.000Z"
}
}
```
Combine fraud detection with the [Webhooks](/docs/webhooks/overview) integration to automatically update your CRM or alert your team when flags are raised.
# Install Tracking
Source: https://agentref.co/docs/merchant-guide/install-tracking
Add the AgentRef tracking script to your website to capture affiliate clicks and attribute conversions.
The tracking script is a lightweight JavaScript snippet that runs on your domain. It captures affiliate referral codes from URL parameters, sets first-party attribution cookies, and passes referral metadata to Stripe during checkout.
## Quick install
Add this script tag to every page where you want to track affiliate visits. Place it in your `` or before the closing `` tag:
```html theme={null}
```
Replace `YOUR_PROGRAM_ID` with your program's ID from the dashboard or API.
That's it. For most setups, this single script tag handles everything.
## Framework examples
```html Plain HTML theme={null}
My App
```
```tsx Next.js (App Router) theme={null}
// app/layout.tsx
import Script from 'next/script';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
{children}
);
}
```
```tsx React (Vite / CRA) theme={null}
// src/App.tsx
import { useEffect } from 'react';
function App() {
useEffect(() => {
const script = document.createElement('script');
script.src =
'https://www.agentref.co/api/tracking/script.js?pid=YOUR_PROGRAM_ID';
script.defer = true;
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
}, []);
return {/* Your app */}
;
}
```
## What the script does
When loaded, the tracking script performs these steps automatically:
1. **Reads URL parameters.** Checks for `via`, `ref`, `r`, `a`, and any custom tracking parameter aliases you've configured. If found, this is an affiliate referral.
2. **Sets first-party cookies.** Stores attribution data on your domain:
* `agentref_cid` – a click identifier used for conversion matching
* `agentref_src` – the traffic source (e.g., `link`, `coupon`)
* `agentref_pid` – the active program ID
3. **Records the click.** Sends a click event to AgentRef's tracking endpoint. This appears in your analytics immediately.
4. **Exposes `window.AgentRef`.** Provides a client-side API for reading referral metadata during checkout.
The script uses only first-party cookies on your domain. It does not use third-party cookies, fingerprinting, or local storage.
## Passing metadata to Stripe
The tracking script sets cookies, but conversions are matched through Stripe metadata. How metadata reaches Stripe depends on your checkout integration:
### Stripe Payment Links and Buy Buttons
No additional server code needed. The tracking script automatically instruments Stripe-hosted surfaces with the click token via `client_reference_id`.
### Server-created Checkout Sessions
For Checkout Sessions you create on your backend, read the metadata from the tracking script on the client side and send it to your server:
```javascript Client-side theme={null}
// Read referral context captured by the tracking script
const metadata = window.AgentRef.getCheckoutMetadata();
// Returns: { agentref_cid: "clk_abc123", agentref_pid: "550e8400-e29b-41d4-a716-446655440000", agentref_source: "redirect" } or {} if no referral
// Send to your server with the checkout request
const response = await fetch('/api/create-checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ metadata }),
});
```
```javascript Server-side (Node.js) theme={null}
// Include metadata in your Stripe Checkout Session
const session = await stripe.checkout.sessions.create({
line_items: [{ price: 'price_xxx', quantity: 1 }],
mode: 'subscription',
metadata: metadata, // top-level metadata
subscription_data: { metadata: metadata }, // subscription metadata
success_url: 'https://yoursite.com/success',
cancel_url: 'https://yoursite.com/cancel',
});
```
For subscriptions, always set metadata on **both** the session and `subscription_data`. This ensures AgentRef can attribute the initial checkout and all recurring invoice payments.
### Direct charges (Payment Intents)
If you create Payment Intents directly instead of using Checkout Sessions:
```javascript theme={null}
const paymentIntent = await stripe.paymentIntents.create({
amount: 2000,
currency: 'usd',
metadata: metadata, // Include agentref_cid
});
```
## Verify your installation
After installing the script, verify that tracking is working correctly.
### Browser check
1. Open your website in a browser.
2. Open DevTools > **Network** tab.
3. Look for a request to `agentref.co/api/tracking/script.js` – it should return `200`.
4. Visit your site through a test referral link (create one in the dashboard).
5. Check DevTools > **Application > Cookies** for `agentref_cid` and `agentref_pid`.
### API check
Use the tracking status endpoint to verify your setup programmatically:
```bash cURL theme={null}
curl https://www.agentref.co/api/v1/programs/PROGRAM_ID/tracking/status \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
```javascript Node.js theme={null}
const status = await client.tracking.getProgramStatus('PROGRAM_ID');
console.log(status.status.trackingScriptVerified); // true
console.log(status.status.stripeConnected); // true
console.log(status.stats.clicksLast7Days.total); // number of clicks
```
```python Python theme={null}
status = client.tracking.get_program_status("PROGRAM_ID")
print(status["status"]["trackingScriptVerified"]) # True
print(status["status"]["stripeConnected"]) # True
print(status["stats"]["clicksLast7Days"]["total"]) # number of clicks
```
The response includes:
| Field | Description |
| ------------------------ | ------------------------------------------------------------- |
| `stripeConnected` | Whether Stripe is connected to this program. |
| `trackingScriptVerified` | Whether the tracking script has been detected on your domain. |
| `websiteConfigured` | Whether a landing page URL is set. |
| `clicksLast7Days` | Click counts broken down by type (redirect, direct). |
| `conversionsLast30Days` | Conversion counts broken down by attribution method. |
## Troubleshooting
| Problem | Solution |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Script returns 404 | Double-check your program ID in the `pid` parameter. |
| Cookies not being set | Make sure the script is loading on the same domain where users check out. Cross-domain cookies will not work. |
| `window.AgentRef` is undefined | The script has not loaded yet. Make sure you access it after the script loads (e.g., on a button click, not immediately on page load). |
| Conversions not appearing | Verify that `agentref_cid` is present in your Stripe session metadata. Check Stripe's event logs to confirm the metadata is attached. |
| Ad blockers blocking the script | The script is served from `agentref.co`. Some aggressive ad blockers may block it. Consider using a [custom tracking domain](/docs/tracking/how-tracking-works) for better reliability. |
## What's next
Deep dive into the tracking architecture, cookie mechanics, and attribution logic.
Start recruiting affiliates to your program.
# Invite Affiliates
Source: https://agentref.co/docs/merchant-guide/invite-affiliates
Create invite links, manage affiliate approvals, and grow your affiliate program.
Once your program is set up and tracking is installed, you need affiliates. AgentRef supports two channels for affiliate recruitment: **invite links** (you reach out directly) and the **marketplace** (affiliates discover you).
This page covers invite links and affiliate lifecycle management.
## Create an invite link
Invite links are unique URLs you share with potential affiliates. When someone visits the link, they can sign up and join your program.
```bash cURL theme={null}
curl -X POST https://www.agentref.co/api/v1/programs/PROGRAM_ID/invites \
-H "Authorization: Bearer ak_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Partner outreach - March",
"isPublic": false,
"usageLimit": 50,
"expiresInDays": 30
}'
```
```javascript Node.js theme={null}
import { AgentRef } from 'agentref';
const client = new AgentRef({ apiKey: 'ak_live_YOUR_KEY' });
const invite = await client.programs.createInvite('PROGRAM_ID', {
name: 'Partner outreach - March',
isPublic: false,
usageLimit: 50,
expiresInDays: 30,
});
console.log(invite.token); // Share this link with affiliates
```
```python Python theme={null}
from agentref import AgentRef
client = AgentRef(api_key="ak_live_YOUR_KEY")
invite = client.programs.create_invite(
"PROGRAM_ID",
name="Partner outreach - March",
is_public=False,
usage_limit=50,
expires_in_days=30,
)
print(invite.token) # Share this link with affiliates
```
### Invite options
| Option | Type | Default | Description |
| ---------------- | ------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | – | Internal label for the invite. Not shown to affiliates. Max 100 characters. |
| `email` | string | – | If set, the invite is tied to a specific email address. Only that email can claim it. |
| `isPublic` | boolean | `false` | Whether anyone with the link can use it, or only the specified email. |
| `usageLimit` | integer | – | Maximum number of times the invite can be claimed. Range: 1–1,000. |
| `expiresInDays` | integer | – | Days until the invite expires. Range: 1–365. |
| `trackingCode` | string | – | Pre-assign a custom tracking code to the affiliate (e.g., `partner-sarah`). Alphanumeric, hyphens, underscores. 3–50 characters. |
| `skipOnboarding` | boolean | `false` | Skip the onboarding flow when the affiliate claims the invite. Useful for automated integrations. |
## Public vs. private invites
**Private invites** (`isPublic: false`) are intended for specific people. If you include an `email`, only that email address can claim the invite. This is the best approach for one-on-one outreach to influencers, partners, or existing customers.
**Public invites** (`isPublic: true`) can be claimed by anyone with the link. Set a `usageLimit` and `expiresInDays` to control usage. This is useful for embedding an "Become an affiliate" link on your website or sharing in newsletters.
For open enrollment on your website, consider listing your program on the [marketplace](/docs/merchant-guide/marketplace) instead. It gives affiliates a professional discovery experience and handles applications automatically.
## List invites for a program
```bash cURL theme={null}
curl https://www.agentref.co/api/v1/programs/PROGRAM_ID/invites \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
```javascript Node.js theme={null}
const invites = await client.programs.listInvites('PROGRAM_ID');
console.log(invites);
```
```python Python theme={null}
invites = client.programs.list_invites("PROGRAM_ID")
print(invites)
```
Invite revocation is currently exposed through Merchant MCP as `revoke_invite` and in the dashboard workflow. The REST SDK can create and list program invites, but it does not expose a revoke method yet.
## Applications and approval
When someone joins through an invite or applies from the marketplace, their approval path depends on your program settings.
### Auto-approve
With `autoApproveAffiliates: true`, eligible applicants are approved immediately and become affiliate memberships. They can start sharing links right away.
### Manual review
With `autoApproveAffiliates: false`, applicants enter the applications queue. Review applications from the dashboard or through the Applications API:
```bash List applications theme={null}
curl "https://www.agentref.co/api/v1/applications?programId=PROGRAM_ID&status=pending" \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
```bash Approve application theme={null}
curl -X POST "https://www.agentref.co/api/v1/applications/APPLICATION_ID/approve" \
-H "Authorization: Bearer ak_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: approve-application-001" \
-d '{"note": "Relevant audience and complete profile."}'
```
```bash Decline application theme={null}
curl -X POST "https://www.agentref.co/api/v1/applications/APPLICATION_ID/decline" \
-H "Authorization: Bearer ak_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: decline-application-001" \
-d '{"note": "Not a fit for this program."}'
```
Use `POST /api/v1/applications/{id}/block` when an application should be blocked from future review.
## Affiliate lifecycle
Every affiliate goes through a defined set of states:
```mermaid theme={null}
graph LR
A[invited or marketplace apply] --> B[application pending]
B -->|Approve| C[affiliate active]
B -->|Decline| E[application declined]
B -->|Block| D[blocked]
C -->|Misconduct| D
D -->|Unblock affiliate| C
```
| Status | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Invited** | An invite has been created but not yet claimed. |
| **Application pending** | The applicant is waiting for merchant review. |
| **Active** | The affiliate is approved and can generate referral links, drive traffic, and earn commissions. |
| **Blocked** | The affiliate has been blocked. Their referral links stop tracking and no new commissions are generated. Existing pending commissions may be rejected. |
### Unblock an affiliate
If you blocked an affiliate by mistake, you can unblock them:
```bash cURL theme={null}
curl -X POST https://www.agentref.co/api/v1/affiliates/AFFILIATE_ID/unblock \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
```javascript Node.js theme={null}
await client.affiliates.unblock('AFFILIATE_ID');
```
```python Python theme={null}
client.affiliates.unblock("AFFILIATE_ID")
```
## List affiliates
View all affiliates across your programs or filter by program:
```bash cURL theme={null}
# All affiliates
curl https://www.agentref.co/api/v1/affiliates \
-H "Authorization: Bearer ak_live_YOUR_KEY"
# Filter by program
curl https://www.agentref.co/api/v1/programs/PROGRAM_ID/affiliates \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
```javascript Node.js theme={null}
// All affiliates
const affiliates = await client.affiliates.list();
// By program
const programAffiliates = await client.programs.listAffiliates('PROGRAM_ID');
```
```python Python theme={null}
# All affiliates
affiliates = client.affiliates.list()
# By program
program_affiliates = client.programs.list_affiliates("PROGRAM_ID")
```
## What's next
List your program in the public marketplace for organic affiliate discovery.
Understand how conversions are tracked and attributed.
# Marketing Resources
Source: https://agentref.co/docs/merchant-guide/marketing-resources
Publish affiliate-ready files, social posts, swipe copy, and external links for your program.
Marketing Resources let merchants give affiliates the material they need to promote a program without sending files manually. Affiliates see only published resources for programs they belong to, and AI agents can retrieve the same material through MCP.
## Resource model
| Item | Purpose |
| --------------- | -------------------------------------------------------------------------------------------------------------------------- |
| **Collections** | Group related campaign assets, launch kits, or seasonal material. Collections are containers, not `resource_type` records. |
| `file` | Downloadable files such as images, videos, PDFs, or media bundles. |
| `social_post` | Affiliate-ready post copy with platform tags, optional media, instructions, and disclosure guidance. |
| `swipe_copy` | Reusable text snippets for newsletters, posts, ads, or landing pages. |
| `external_link` | Links to external brand folders, product pages, or campaign material. |
Resources move through three statuses:
| Status | Meaning |
| ----------- | ----------------------------------------------------------------------------------------- |
| `draft` | Visible to merchants only. Use this while preparing or reviewing material. |
| `published` | Visible to approved affiliates and affiliate MCP clients. |
| `archived` | Hidden from affiliates and removed from active merchant lists unless explicitly filtered. |
## Supported files and limits
AgentRef accepts PNG, JPEG, WebP, GIF, MP4, QuickTime, WebM, and PDF files.
| Limit | Value |
| --------------------------------- | ------ |
| Standard file upload | 50 MB |
| Social post image | 50 MB |
| Social post video | 200 MB |
| Total media per social post | 500 MB |
| Media attachments per social post | 10 |
| Items per collection | 100 |
Storage and notification limits depend on your billing tier:
| Tier | Storage | Collections | Affiliate notifications |
| ------- | ------- | ------------------------ | ----------------------- |
| Free | 250 MB | 1 collection per program | 0 per rolling week |
| Starter | 2 GB | Unlimited | 2 per rolling week |
| Growth | 10 GB | Unlimited | 5 per rolling week |
| Pro | 50 GB | Unlimited | 10 per rolling week |
## Social post templates
Social posts can include placeholders that are rendered for each affiliate:
| Placeholder | Rendered value |
| -------------------- | ----------------------------------------------------- |
| `{{affiliate_link}}` | The selected affiliate tracking link for the program. |
| `{{affiliate_code}}` | The affiliate's referral code. |
| `{{program_name}}` | The program name. |
AgentRef also returns disclosure guidance with rendered social posts. If a post includes a referral link, affiliates should include a clear disclosure such as `#ad` or equivalent wording that fits the platform and your policy.
## Merchant workflow
1. Create or select a collection for the campaign.
2. Upload files, create social posts, add swipe copy, or attach external links.
3. Review the material in `draft` status.
4. Publish the resource when it is ready for affiliates.
5. Optionally notify affiliates when a published resource should be used immediately.
## Surface coverage
Marketing Resources are broader in the dashboard and MCP than they are in the REST API and SDKs. REST and SDKs currently cover listing, social posts, social-post media, status changes, notifications, and download URLs. MCP additionally exposes collection, external-link, generic file-upload, and URL-import workflows.
| Capability | Dashboard | REST API | Node/Python SDKs | Merchant MCP |
| ------------------------------------------------------ | --------- | -------- | ---------------- | ------------ |
| List resources and get details | Yes | Yes | Yes | Yes |
| Create and update social posts | Yes | Yes | Yes | Yes |
| Upload, replace, reorder, and remove social-post media | Yes | Yes | Yes | Yes |
| Publish, unpublish, archive, notify affiliates | Yes | Yes | Yes | Yes |
| Create merchant download URLs | Yes | Yes | Yes | Yes |
| Create or update collections | Yes | No | No | Yes |
| Create external links | Yes | No | No | Yes |
| Generic file upload sessions | Yes | No | No | Yes |
| Import a resource from a URL | Yes | No | No | Yes |
| Create or update swipe copy | Yes | No | No | No |
Marketing Resources do not currently emit webhook events. Use API/MCP reads or dashboard activity when you need to inspect resource state.
## REST API
Merchant Marketing Resources use the `marketing_resources:read` and `marketing_resources:write` scopes.
| Action | Endpoint |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| List resources | `GET /api/v1/programs/{id}/marketing-resources` |
| Create social post | `POST /api/v1/programs/{id}/marketing-resources/social-posts` |
| Update social post | `PATCH /api/v1/marketing-resources/social-posts/{resourceId}` |
| Create media upload session | `POST /api/v1/marketing-resources/social-posts/{resourceId}/media/upload-sessions` |
| Complete media upload | `POST /api/v1/marketing-resources/social-posts/{resourceId}/media/complete` |
| Update or remove media | `PATCH /api/v1/marketing-resources/social-posts/{resourceId}/media/{mediaId}` or `DELETE /api/v1/marketing-resources/social-posts/{resourceId}/media/{mediaId}` |
| Reorder media | `POST /api/v1/marketing-resources/social-posts/{resourceId}/media/reorder` |
| Replace media | `POST /api/v1/marketing-resources/social-posts/{resourceId}/media/{mediaId}/replace` |
| Publish, unpublish, archive, notify | `POST /api/v1/marketing-resources/{resourceId}/{action}` |
| Create download URL | `POST /api/v1/marketing-resources/download-url` |
```bash cURL theme={null}
curl "https://www.agentref.co/api/v1/programs/PROGRAM_ID/marketing-resources?kind=social_posts&status=published" \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
```bash Create social post theme={null}
curl -X POST "https://www.agentref.co/api/v1/programs/PROGRAM_ID/marketing-resources/social-posts" \
-H "Authorization: Bearer ak_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: launch-post-001" \
-d '{
"title": "Launch announcement",
"body": "I am sharing {{program_name}} because it helps teams launch affiliate programs faster. Try it here: {{affiliate_link}}",
"platforms": ["linkedin", "x"],
"status": "draft"
}'
```
## MCP tools
Merchant MCP exposes the richest Marketing Resources workflow:
`list_marketing_resources`, `get_marketing_resource`, `create_marketing_collection`, `update_marketing_collection`, `publish_marketing_resource`, `unpublish_marketing_resource`, `archive_marketing_resource`, `create_marketing_social_post`, `update_marketing_social_post`, `create_marketing_social_post_media_upload_session`, `complete_marketing_social_post_media_upload`, `remove_marketing_social_post_media`, `update_marketing_social_post_media`, `reorder_marketing_social_post_media`, `replace_marketing_social_post_media`, `create_marketing_resource_download_url`, `create_marketing_external_link`, `create_marketing_upload_session`, `complete_marketing_upload_session`, `import_marketing_resource_from_url`, and `notify_marketing_resource_affiliates`.
Use the merchant MCP endpoint for these tools:
```text theme={null}
https://www.agentref.co/api/mcp/merchant
```
## What's next
Use AI agents to create, publish, and retrieve marketing resources.
See what affiliates can access and render.
# Marketplace
Source: https://agentref.co/docs/merchant-guide/marketplace
List your program in the AgentRef marketplace so affiliates can discover and apply to it.
The AgentRef marketplace is a feature-flagged public directory where affiliates browse and apply to affiliate programs. Listing your program there gives you passive inbound reach – affiliates find you rather than waiting for your invites.
During the current closed beta, marketplace discovery is gated by AgentRef's `marketplace_public` flag. Public listing settings can still be prepared, but live discovery returns an empty closed-beta response until the marketplace is opened.
## How it works
Programs listed as `public` appear in the marketplace with their commission rate, category, EPC (earnings per click), and conversion rate. Affiliates can filter by category, minimum commission, and performance metrics. When an affiliate applies, they either get auto-approved instantly (if your program has `autoApproveAffiliates: true`) or land in your pending applications queue for manual review.
A program must be fully set up – Stripe connected, tracking installed, and at least one active affiliate – before it becomes eligible for public listing.
## List your program
In the dashboard go to **Programs**, select your program, then open **Settings > Marketplace**. Set visibility to **Public** and fill in a short description and category. Save.
If your program is not yet fully ready, AgentRef will show which setup steps are incomplete. Fix those first – an incomplete program cannot appear publicly even if you set visibility to public.
You can also update marketplace settings via the programs API. Use `marketplaceStatus: "draft"` while preparing the listing, `marketplaceStatus: "public"` when it should appear publicly, or `marketplaceStatus: "private"` to hide it. Use `applicationAccess: "open"` for marketplace applications and `applicationAccess: "invite_only"` for invite-only programs.
## Managing applications
When an affiliate applies from the marketplace, the application appears in **Programs > \[Your Program] > Applications**. Each application shows the affiliate's name, their optional introductory message, and the date applied.
* **Auto-approved** – if your program has `autoApproveAffiliates: true`, the affiliate is added immediately and receives their affiliate link.
* **Pending review** – if auto-approve is off, approve, decline, or block the application from the dashboard or Applications API.
Enabling `autoApproveAffiliates` reduces drop-off. Affiliates who apply and get an immediate link are far more likely to start promoting than those who wait for manual approval.
## API reference
### List marketplace programs
Returns publicly listed programs when `marketplace_public` is enabled. During closed beta, the endpoint returns an empty list with `meta.note: "closed_beta"`. Available to affiliate API keys (scope `marketplace:read`).
```bash cURL theme={null}
curl "https://www.agentref.co/api/v1/marketplace/programs?sort=epc&minCommission=20&limit=20" \
-H "Authorization: Bearer ak_aff_YOUR_KEY"
```
```javascript JavaScript theme={null}
const result = await fetch(
'https://www.agentref.co/api/v1/marketplace/programs?sort=epc&minCommission=20&limit=20',
{ headers: { Authorization: 'Bearer ak_aff_YOUR_KEY' } },
).then((r) => r.json());
```
```python Python theme={null}
import httpx
result = httpx.get(
"https://www.agentref.co/api/v1/marketplace/programs",
headers={"Authorization": "Bearer ak_aff_YOUR_KEY"},
params={"sort": "epc", "minCommission": 20, "limit": 20},
).json()
```
**Query parameters**
| Parameter | Type | Description |
| ----------------- | ------ | ----------------------------------------------------- |
| `category` | string | Filter by category (e.g. `saas`, `ecommerce`) |
| `minCommission` | number | Minimum commission percentage |
| `minEpc` | number | Minimum earnings per click (USD) |
| `sort` | string | `epc` \| `conversionRate` \| `commission` \| `newest` |
| `limit` | number | Max results (1–50, default 20) |
| `page` / `offset` | number | Pagination |
### Apply to a program
Submits an affiliate application. Requires an affiliate API key (scope `marketplace:apply`).
```bash cURL theme={null}
curl -X POST "https://www.agentref.co/api/v1/marketplace/apply/PROGRAM_ID" \
-H "Authorization: Bearer ak_aff_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"message": "I run a newsletter focused on SaaS tools with 8k subscribers."}'
```
```javascript JavaScript theme={null}
const application = await fetch(
'https://www.agentref.co/api/v1/marketplace/apply/PROGRAM_ID',
{
method: 'POST',
headers: {
Authorization: 'Bearer ak_aff_YOUR_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: 'I run a newsletter focused on SaaS tools with 8k subscribers.',
}),
},
).then((r) => r.json());
```
```python Python theme={null}
application = httpx.post(
"https://www.agentref.co/api/v1/marketplace/apply/PROGRAM_ID",
headers={"Authorization": "Bearer ak_aff_YOUR_KEY"},
json={"message": "I run a newsletter focused on SaaS tools with 8k subscribers."},
).json()
```
The response includes a `status` field (`auto_approved` or `pending`) and a `nextSteps` array describing what the affiliate should do next.
Applying twice to the same program returns the existing application rather than creating a duplicate. The HTTP response code will be `200` instead of `201` in that case.
## What's next
Invite affiliates directly without waiting for marketplace applications.
See how commissions are created when affiliates drive sales.
# Payouts & Billing
Source: https://agentref.co/docs/merchant-guide/payouts-billing
How affiliate payouts work and how AgentRef billing and plan limits are managed.
## How Payouts Work
When affiliates earn commissions, the money flows through a simple lifecycle:
```
Conversion Created → Commission Pending → Payout Created → Payout Processing → Payout Completed
```
### Payout Lifecycle
| Status | Description |
| ------------ | ---------------------------------------------------- |
| `pending` | Commission earned, waiting to reach payout threshold |
| `processing` | Merchant has started the external payment step |
| `completed` | Merchant marked the payout as paid |
| `failed` | External payment attempt failed and can be retried |
### Payout Thresholds
Affiliates become payout-eligible when approved commissions reach the threshold. You then create the manual payout record in AgentRef. Default threshold: **\$50 USD**. Configurable per program.
### Manual payout workflow
This is the active launch contract for payouts today.
Affiliates save payout details in AgentRef using PayPal or bank transfer fields. Merchants then:
1. Create a payout record in AgentRef
2. Export or prepare the payout externally
3. Send the payment outside AgentRef
4. Mark the payout as `processing`, `completed`, or `failed`
AgentRef tracks the payout state and audit trail, but does not move money on the merchant's behalf.
REST and SDKs can list pending affiliates, create payout records, list payout history, and read payout stats. Marking payout status, listing upcoming payouts, and exporting payout CSV files are currently available through Merchant MCP and the dashboard workflow.
## Managing Payouts via API
### List Payouts
```javascript Node.js theme={null}
const payouts = await client.payouts.list({
programId: 'prg_abc123',
status: 'pending',
})
```
```python Python theme={null}
payouts = client.payouts.list(
program_id="prg_abc123",
status="pending",
)
```
```bash cURL theme={null}
curl "https://www.agentref.co/api/v1/payouts?programId=prg_abc123&status=pending" \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
### List Pending Affiliates
```javascript Node.js theme={null}
const pending = await client.payouts.listPending({
programId: 'PROGRAM_ID',
})
```
```python Python theme={null}
pending = client.payouts.list_pending(program_id="PROGRAM_ID")
```
```bash cURL theme={null}
curl "https://www.agentref.co/api/v1/payouts/pending?programId=PROGRAM_ID" \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
### Create a Payout Record
```javascript Node.js theme={null}
const payout = await client.payouts.create({
affiliateId: 'AFFILIATE_ID',
programId: 'PROGRAM_ID',
method: 'paypal',
notes: 'April payout',
}, {
idempotencyKey: 'payout-affiliate-april',
})
```
```python Python theme={null}
payout = client.payouts.create(
affiliate_id="AFFILIATE_ID",
program_id="PROGRAM_ID",
method="paypal",
notes="April payout",
idempotency_key="payout-affiliate-april",
)
```
```bash cURL theme={null}
curl -X POST "https://www.agentref.co/api/v1/payouts" \
-H "Authorization: Bearer ak_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: payout-affiliate-april" \
-d '{
"affiliateId": "AFFILIATE_ID",
"programId": "PROGRAM_ID",
"notes": "April payout"
}'
```
### Payout Statistics
```javascript Node.js theme={null}
const stats = await client.payouts.stats({
programId: 'prg_abc123',
})
// { totalPaid, totalPending, totalFailed, count }
```
```bash cURL theme={null}
curl "https://www.agentref.co/api/v1/payouts/stats?programId=prg_abc123" \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
***
## AgentRef Billing
AgentRef starts with a free tier and supports paid Starter, Growth, and Pro plans. Billing is managed from the dashboard and through the Billing API.
### How It Works
* **\$0 to start** – no upfront cost, no credit card required
* **Upgrade when you need higher limits** – paid plans unlock more storage, notifications, and operating room
* **No automatic downgrade** – plan changes are merchant-initiated
### Billing API
```javascript Node.js theme={null}
// View current billing status
const billing = await client.billing.current()
// View available tiers
const tiers = await client.billing.tiers()
```
```bash cURL theme={null}
# View billing status
curl "https://www.agentref.co/api/v1/billing" \
-H "Authorization: Bearer ak_live_YOUR_KEY"
# View tiers
curl "https://www.agentref.co/api/v1/billing/tiers" \
-H "Authorization: Bearer ak_live_YOUR_KEY"
```
Billing is handled automatically through Stripe. You can view your usage and invoices in the AgentRef dashboard under **Settings → Billing**.
# Best Practices
Source: https://agentref.co/docs/resources/best-practices
Recommendations for getting the most out of AgentRef.
## For Merchants
### Program Setup
* **Set competitive commission rates** – research what similar products offer. 20-30% recurring is standard for SaaS.
* **Use reasonable cookie durations** – 60-90 days is typical. Too short means lost attributions; too long rarely adds value.
* **Write clear program descriptions** – affiliates (and their AI agents) need to understand your product and terms quickly.
### Tracking
* **Install tracking on all pages** – not just the landing page. Visitors may enter from blog posts, docs, or pricing pages.
* **Test before launch** – use debug mode (`?agentref_debug=1`) to verify cookies are set and clicks are recorded.
* **Use Stripe Checkout integration** – it's the most reliable way to attribute conversions. AgentRef does not require a separate conversion-reporting call when Stripe metadata is present.
### Fraud Prevention
* **Review flags promptly** – unresolved flags can lead to disputed payouts.
* **Start with manual approval for new affiliates** – auto-approve only after they've built a track record.
* **Monitor click-to-conversion ratios** – very high or very low ratios can indicate problems.
### Affiliate Relations
* **Respond to applications quickly** – affiliates will move on to competing programs if they wait too long.
* **Provide marketing materials** – make it easy for affiliates to promote your product correctly.
* **Pay on time** – nothing kills an affiliate relationship faster than delayed or missed payouts.
## For Affiliates
### Getting Started
* **Choose programs you genuinely use** – authentic recommendations convert better than generic promotions.
* **Use sub-IDs to track placements** – know which blog posts, videos, or channels drive the most conversions.
* **Disclose affiliate relationships** – it's legally required in most jurisdictions and builds trust with your audience.
### Optimization
* **Link from contextually relevant content** – a referral in a product review converts better than a sidebar banner.
* **Use deep links when possible** – link directly to pricing or signup pages, not just the homepage.
* **Track your metrics** – use the dashboard or API to monitor clicks, conversions, and earnings regularly.
## For AI Agents
### Integration
* **Use the MCP Server** – it provides the richest interface for agent interaction across merchant setup, growth, payouts, and affiliate workflows.
* **Read `llms.txt` first** – it gives you a concise overview of all available endpoints and capabilities.
* **Use idempotency keys** – when creating programs, invites, payouts, links, or resources, include idempotency keys to prevent duplicates.
### Automation
* **Check affiliate status before promoting** – verify your affiliate account is approved and active before generating referral links.
* **Monitor earnings programmatically** – use the stats endpoints to track performance and adjust strategies.
* **Handle errors gracefully** – the API uses standard HTTP codes. Implement retry logic for transient failures (429, 500, 503).
The [Agent Setup Guide](/docs/getting-started/agent-setup) has detailed examples for configuring AI agents with AgentRef.
# Changelog
Source: https://agentref.co/docs/resources/changelog
Recent updates and changes to AgentRef.
## May 2026
### Marketing Resources
* Added Marketing Resources for merchants and affiliates, including campaign collections, files, social posts, swipe copy, external links, and affiliate rendering
* Added merchant MCP tools for creating, publishing, archiving, downloading, importing, and notifying affiliates about marketing resources
* Added affiliate MCP tools for listing resources, rendering social posts with affiliate placeholders, and creating download URLs
### Applications and Affiliate Workspace
* Added a dedicated Applications lifecycle for marketplace and portal applicants
* Added affiliate workspace overview and program detail endpoints
* Updated affiliate link creation to use `destination_path` and optional `custom_slug`
### SDKs
* Node SDK and Python SDK updated to v5.1.2
* Added SDK coverage for Applications, Marketing Resources, Onboarding, Tracking, Invites, Marketplace, and the expanded Affiliate Workspace
* Aligned notification and payout-info access as top-level SDK resources
## April 2026
### MCP Release Contract & Agent Setup
* Rebuilt the MCP release surface around persona-specific merchant and affiliate endpoints
* Added OAuth discovery, dynamic client registration, and auth-first remote MCP flows
* Verified onboarding flows for Claude Code, Claude App, Codex, Cursor, and OpenClaw (`mcporter`)
* Added the first official AgentRef OpenClaw skill as a REST fallback for non-MCP OpenClaw workflows
## March 2026
### MCP Server & Agent Integration
* Added MCP Server with 24 tools across onboarding, merchant, and affiliate categories
* Node SDK and Python SDK updated for REST API v1
* Added CLI tool for terminal-based program management
### Webhook System
* Launched outbound webhooks with 13 event types
* HMAC-SHA256 signature verification
* Automatic retry with exponential backoff (8 retry attempts)
### Fraud Detection
* Automated fraud flag system with trust tiers
* Suspicious click pattern detection
* Self-referral detection
### Tracking Enhancements
* Debug mode (`?agentref_debug=1`) for tracking troubleshooting
* Consent mode for GDPR compliance
* UTM parameter tracking (`utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term`)
* Ad-click ID tracking (`fbclid`, `gclid`, `msclkid`, `ttclid`)
* Sub-ID support (`sub1` through `sub5`)
### Marketplace
* Public program directory for affiliate discovery
* In-app applications and approval workflow
### Billing
* Usage-based billing with \$0 entry
* Manual plan selection with Free, Starter, Growth, and Pro tiers
***
This changelog covers product updates. For API and SDK changes, see the changelogs in the [Node SDK](https://github.com/agentref/agentref-node) and [Python SDK](https://github.com/agentref/agentref-python) repositories.
# Glossary
Source: https://agentref.co/docs/resources/glossary
Key terms and concepts used throughout AgentRef.
## A
**Affiliate**
A person, company, or AI agent that promotes a merchant's product in exchange for commissions on successful referrals.
**Affiliate Code / Referral Code**
A unique identifier assigned to each affiliate (e.g., `jane`, `techreviewer`). Used in tracking links as the `ref` parameter.
**API Key**
A secret token used to authenticate API requests. Merchant keys start with `ak_live_`, and affiliate keys start with `ak_aff_`.
**Attribution**
The process of linking a conversion back to the affiliate who referred the customer.
## C
**Click**
A recorded visit from a tracking link. Each click captures the affiliate, referrer URL, landing URL, UTM parameters, and sub-IDs.
**Commission**
The amount an affiliate earns when a referred customer makes a purchase. Can be percentage-based or a fixed amount.
**Conversion**
A successful referral event – typically a purchase or signup – that earns the affiliate a commission.
**Cookie Duration / Cookie Window**
The number of days a tracking cookie remains valid after a click. Conversions within this window are attributed to the affiliate.
## F
**Fraud Flag**
An automated alert when AgentRef detects suspicious affiliate activity (unusual click patterns, self-referrals, etc.).
## I
**Idempotency Key**
A unique key sent with API requests to prevent duplicate operations. If you retry a request with the same key, you get the same result.
## L
**Last-Click Attribution**
The attribution model used by AgentRef. The last affiliate link clicked before conversion gets the commission.
## M
**MCP (Model Context Protocol)**
An open standard for connecting AI agents to external tools and data. AgentRef provides an OAuth-ready MCP server with tools, resources, and prompts for merchant and affiliate workflows.
**Merchant**
A SaaS company or business that runs an affiliate program on AgentRef.
**Marketplace**
A feature-flagged public directory where affiliates can discover and apply to affiliate programs. During closed beta, discovery returns an empty closed-beta response instead of live program data.
## P
**Payout**
A payout record that tracks an external commission payment from the merchant to the affiliate.
**Payout Threshold**
The minimum commission balance required before a payout is created (default: \$50).
**Program**
An affiliate program created by a merchant. Defines commission rates, cookie duration, and payout terms.
## S
**Sub-ID**
Custom tracking parameters (`sub1` through `sub5`) that affiliates can append to their links for their own analytics.
## T
**Tracking Script**
A JavaScript snippet installed on the merchant's website that records affiliate clicks and sets tracking cookies.
**Trust Tier**
A classification assigned to affiliates based on their history. Determines whether conversions are auto-approved or require manual review.
## W
**Webhook**
An HTTP callback that AgentRef sends to your server when events occur (conversions, payouts, affiliate activity, etc.).
# Security
Source: https://agentref.co/docs/resources/security
How AgentRef protects your data and what security measures are in place.
## Infrastructure
* **Managed cloud hosting** – production traffic runs on managed infrastructure with network-level DDoS protections.
* **Encrypted data stores** – application data is encrypted at rest and in transit.
* **TLS everywhere** – public application, API, MCP, and webhook traffic uses HTTPS.
* **Operational monitoring** – production systems are monitored for availability, abuse signals, and delivery failures.
## Authentication
### API Keys
* All API keys are hashed before storage – we never store plaintext keys
* Keys are prefixed by type for easy identification:
* `ak_live_` – merchant keys
* `ak_aff_` – affiliate keys
* Keys are scoped – each key only has access to the permissions you grant
* Keys can be revoked instantly from the dashboard
### Webhook Signatures
* All outbound webhooks are signed with HMAC-SHA256
* Signing secrets are derived per-endpoint (not shared across endpoints)
* Timestamp validation prevents replay attacks
* See [Signature Verification](/docs/webhooks/signature-verification) for implementation details
## Data Protection
### What We Store
| Data | Purpose | Retention |
| ------------------ | ---------------------- | ----------------------------------- |
| Click data | Attribution tracking | Duration of cookie window + 30 days |
| Conversion data | Commission calculation | Indefinite (business records) |
| Affiliate profiles | Account management | Until account deletion |
| Tracking cookies | Attribution | First-party, configurable duration |
### What We Don't Store
* Credit card numbers (handled entirely by Stripe)
* Customer passwords
* Raw Stripe webhook payloads (processed and discarded)
### Cookies
* **First-party only** – no third-party cookies
* **SameSite=Lax** – prevents CSRF
* **HttpOnly** where applicable
* Cookie names: `agentref_cid`, `agentref_pid`, `agentref_src`, `agentref_vid`, `agentref_ts`
## GDPR & Privacy
* **Consent mode** – tracking script can defer click recording until `AgentRef.setConsent('granted')` is called
* **No cross-site tracking** – all cookies are first-party
* **Data minimization** – we only collect what's needed for attribution
* **Right to deletion** – affiliates and merchants can request account deletion
* See [Consent & GDPR](/docs/tracking/consent-gdpr) for implementation details
## Responsible Disclosure
If you discover a security vulnerability, please email **[security@agentref.co](mailto:security@agentref.co)**. We take all reports seriously and will respond within 48 hours.
Never share API keys in public repositories, client-side code, or support tickets. If a key is compromised, revoke it immediately in the dashboard and create a new one.
# Consent & GDPR
Source: https://agentref.co/docs/tracking/consent-gdpr
How AgentRef's tracking script respects cookie consent before setting any cookies or recording clicks.
AgentRef's tracking script has built-in consent gating. When consent mode is enabled for your program, the script will not set cookies or record clicks until the visitor grants consent. This lets you integrate with any cookie banner without writing custom coordination logic.
## How consent mode works
Consent state is stored in `localStorage` under the key `agentref_consent` with values `'granted'` or `'denied'`. On each page load, the script checks this value before doing anything:
* **No consent stored + consent required** – the script initializes (sets `ready`) but skips cookie writing and click recording. A `consent_pending` warning is added to the debug info.
* **`'granted'`** – tracking proceeds normally.
* **`'denied'`** – any existing AgentRef cookies are cleared and tracking is disabled.
* **Consent not required** – tracking always proceeds regardless of stored consent (the default for programs that have not enabled consent mode).
You enable consent mode in your program's **Settings → Tracking** page. Once enabled, `trackingRequiresConsent` is baked into the served script config for your program.
## Integrating with a cookie consent manager
Listen for Cookiebot's consent event and call `AgentRef.setConsent()`:
```js theme={null}
window.addEventListener('CookiebotOnAccept', function() {
if (window.Cookiebot && window.Cookiebot.consent.statistics) {
window.AgentRef && window.AgentRef.setConsent('granted');
}
});
window.addEventListener('CookiebotOnDecline', function() {
window.AgentRef && window.AgentRef.setConsent('denied');
});
```
Use OneTrust's `OptanonWrapper` callback, which fires after any consent change:
```js theme={null}
function OptanonWrapper() {
// Check if the "Performance Cookies" category is active
// Replace 'C0002' with your actual category ID for analytics/tracking
var groups = window.OnetrustActiveGroups || '';
if (groups.indexOf('C0002') !== -1) {
window.AgentRef && window.AgentRef.setConsent('granted');
} else {
window.AgentRef && window.AgentRef.setConsent('denied');
}
}
```
If you manage consent yourself, call `AgentRef.setConsent()` when the visitor makes a choice:
```js theme={null}
// When user clicks "Accept all"
document.getElementById('accept-btn').addEventListener('click', function() {
window.AgentRef && window.AgentRef.setConsent('granted');
hideBanner();
});
// When user clicks "Reject" or "Accept necessary only"
document.getElementById('reject-btn').addEventListener('click', function() {
window.AgentRef && window.AgentRef.setConsent('denied');
hideBanner();
});
```
`setConsent('granted')` automatically calls `AgentRef.refresh()` internally – tracking initializes immediately without a page reload.
## Deferring tracking with `data-consent`
If you want to prevent the script from running at all until consent is determined (for example, to avoid the script executing before your banner has loaded), you can enable consent mode in program settings rather than using a script attribute. The `trackingRequiresConsent` flag is embedded in the script config served from the endpoint.
There is no `data-consent` HTML attribute – consent gating is controlled entirely through your program settings and the `AgentRef.setConsent()` API.
## What happens without consent
When consent is required and not yet granted:
* No cookies are written (`agentref_cid`, `agentref_pid`, `agentref_src`, `agentref_vid`, `agentref_ts`)
* Clicks are not recorded to AgentRef's backend
* `AgentRef.getCheckoutMetadata()` returns `{}`
* `AgentRef.ready()` callbacks still fire (with empty state), so your checkout code won't hang
When consent is explicitly denied:
* All existing AgentRef cookies are cleared from the visitor's browser
* Tracking remains disabled until consent is granted in a future session
## GDPR compliance summary
When a click is recorded, AgentRef stores:
* The referral code (affiliate identifier)
* The page URL the visitor landed on
* The visitor's IP address (for unique click deduplication – not linked to personal identity)
* User agent string
* Country (derived from IP, not stored as IP)
* UTM parameters and ad-click IDs present in the URL
* Sub-IDs passed by the affiliate
* An anonymous visitor ID generated by the script
No name, email, or other personally identifiable information is collected by the tracking script itself. PII only enters the system when a Stripe payment event fires (from Stripe's data, not AgentRef's collection).
Click records are retained according to your program's cookie duration setting (default 30 days) for active attribution. Conversion records are retained indefinitely as they are financial records tied to affiliate payouts.
The anonymous visitor ID cookie (`agentref_vid`) has a 365-day lifetime. This is a random string with no external linkage – it is used only to deduplicate click counts within AgentRef.
Visitors can delete AgentRef cookies at any time through their browser settings. Since the anonymous visitor ID has no linkage to personal identity, there is no personal data to delete from AgentRef's backend based on a cookie value alone.
If a visitor makes a data deletion request that you need to fulfill, contact AgentRef support with the relevant click tokens or conversion IDs.
No. Click and attribution data is stored in AgentRef systems and shared only with the merchant (you) and the affiliate who drove the click, as part of normal program reporting.
This page describes how AgentRef's technical implementation supports GDPR compliance. It is not legal advice. Consult your legal team to determine the correct consent category and wording for your cookie banner.
# Debug Mode
Source: https://agentref.co/docs/tracking/debug-mode
Inspect tracking state, diagnose attribution issues, and troubleshoot the AgentRef script with built-in debug tools.
Debug mode gives you a console readout of the tracking script's state – what cookies are set, what parameters were detected, and whether anything went wrong during initialization.
## Enabling debug mode
You can enable debug mode in two ways:
**Option 1: URL parameter (temporary, no code change)**
Append `?agentref_debug=1` to any page URL on your site:
```
https://yoursite.com/pricing?via=abc123&agentref_debug=1
```
Open the browser console – you'll see an `[AgentRef]` log entry immediately after the script initializes.
**Option 2: Script attribute (always-on)**
Add `data-debug="true"` to your script tag to log on every page load:
```html theme={null}
```
Do not ship `data-debug="true"` to production. It logs attribution state to the browser console on every page visit, which is noisy for real users and may expose referral context on shared machines.
## What debug mode shows
The console output is the return value of `window.AgentRef.getDebugInfo()`. Here is an example:
```json theme={null}
{
"params": {
"referralParam": "ref",
"referralCode": "abc123",
"clickToken": null,
"debug": true,
"subIds": { "sub1": null, "sub2": null, "sub3": null, "sub4": null, "sub5": null },
"utm": { "utmSource": "twitter", "utmMedium": "social", "utmCampaign": null, "utmTerm": null, "utmContent": null },
"adClickIds": { "gclid": null, "fbclid": null, "msclkid": null, "ttclid": null, "liFatId": null }
},
"cookies": {
"clickToken": "clk_a1b2c3d4e5",
"source": "script_direct",
"programId": "550e8400-e29b-41d4-a716-446655440000",
"visitorId": "v_k8m2n4p6q7",
"timestamp": "1711234567890"
},
"lastResolution": {
"clickToken": "clk_a1b2c3d4e5",
"programId": "550e8400-e29b-41d4-a716-446655440000",
"source": "script_direct"
},
"trackingScope": {
"trackingDomain": "yoursite.com",
"mode": "root"
},
"warnings": []
}
```
### Field reference
| Field | What it tells you |
| --------------------- | ------------------------------------------------------------------------------------- |
| `params.referralCode` | The affiliate code detected in the URL (`null` if none) |
| `params.clickToken` | Pre-existing click token from a redirect URL (`null` for direct clicks) |
| `cookies.clickToken` | The `agentref_cid` cookie value – this is what goes into Stripe metadata |
| `cookies.programId` | The `agentref_pid` cookie – confirms which program the current attribution belongs to |
| `trackingScope.mode` | `"root"` = cookies scoped to root domain; `"host"` = scoped to exact hostname only |
| `warnings` | Array of warning codes if anything went wrong (empty = clean init) |
### Warning codes
| Warning | Meaning |
| ------------------------------------ | ---------------------------------------------------------------- |
| `consent_pending` | Consent is required but not yet granted – tracking was skipped |
| `host_outside_website_domain` | The current hostname doesn't match your program's website domain |
| `cleanup_failed` | URL parameter cleanup via `history.replaceState` failed |
| `stripe_payment_link_rewrite_failed` | Could not append `client_reference_id` to a Stripe Payment Link |
| `direct_click_failed` | The click recording request to AgentRef failed |
| `tracking_verify_failed` | The verification request failed |
## Calling `getDebugInfo()` manually
You can call it any time from the browser console:
```js theme={null}
window.AgentRef.getDebugInfo()
```
This is useful after navigation, after granting consent, or after a manual `AgentRef.refresh()` call – any time you want to see the current state without reloading the page.
## Troubleshooting checklist
1. Open the Network tab in DevTools and filter for `script.js`. Check the response status.
2. A `404` means your `?pid=` value is invalid or the program does not exist. Verify the program ID in your AgentRef dashboard.
3. A `400` response with `// Missing program ID` means the `?pid=` query string is missing entirely.
4. Check for Content Security Policy (CSP) headers blocking `https://www.agentref.co`. Add it to your `script-src` and `connect-src` directives.
1. Enable debug mode and check `params.referralCode` – if it's `null`, no referral parameter was found in the URL. Make sure your affiliate link includes `?via=` or another supported alias.
2. Check `warnings` – `consent_pending` means consent mode is enabled and the visitor hasn't granted consent yet.
3. Check `trackingScope.mode` – if it's `"host"` and your checkout is on a subdomain, cookies may not be shared across subdomains. Set your website URL in program settings to the root domain (e.g., `yoursite.com` not `www.yoursite.com`).
4. If the referral code is present but no click token is in cookies, the click recording request may have failed. Check the Network tab for a failed POST to `/api/tracking/click`.
1. Verify that `getCheckoutMetadata()` returns a non-empty object before checkout. Call it from the console: `window.AgentRef.getCheckoutMetadata()`.
2. Confirm the metadata is being passed to Stripe. Check the session object in your Stripe dashboard – look for `agentref_cid` in the metadata fields.
3. For Payment Links, confirm the link on your page has `data-agentref-instrumented="true"` after the script loads – this attribute is set by the script when it successfully instruments a link.
4. Check that your Stripe Connect integration is active in AgentRef. If the webhook is not receiving events, no conversions will be created.
AgentRef uses last-click attribution. If a visitor clicked multiple affiliate links before purchasing, the most recent click wins.
Coupon codes take priority over click tokens – if the customer uses a coupon linked to a different affiliate, that affiliate gets credit regardless of which link the visitor clicked.
Check the conversion detail in your dashboard to see which attribution method was used (click token vs. coupon code).
# How Tracking Works
Source: https://agentref.co/docs/tracking/how-tracking-works
Conceptual overview of AgentRef's click-to-commission tracking pipeline – cookie-based attribution, Stripe webhook matching, and the full conversion flow.
AgentRef uses a lightweight, first-party JavaScript snippet combined with Stripe webhooks to track the entire affiliate journey. This page explains the full pipeline from click to commission.
## The tracking pipeline
```mermaid theme={null}
graph TD
A["Affiliate shares referral link"] --> B["Visitor clicks link"]
B --> C["Tracking script loads on merchant site"]
C --> D["Script records click + sets first-party cookies"]
D --> E["Visitor browses the site"]
E --> F["Visitor purchases via Stripe"]
F --> G["Stripe webhook fires to AgentRef"]
G --> H["AgentRef matches metadata to click"]
H --> I["Conversion created, commission calculated"]
```
## How clicks are tracked
When a visitor arrives on your site through an affiliate link (e.g., `https://yoursite.com?via=abc123`), the tracking script:
1. **Detects the referral parameter.** Generated links use `via` by default. The script also accepts `ref`, `r`, `a`, and any [custom aliases](/docs/tracking/javascript-snippet#url-parameters) you configure.
2. **Records the click server-side.** The script sends a request to AgentRef's click endpoint with the referral code, page URL, UTM data, and ad-click IDs.
3. **Sets first-party cookies.** On successful click recording, the script stores attribution data in cookies on your domain.
4. **Cleans up the URL.** The referral parameter is removed from the browser address bar so it doesn't leak into bookmarks or shared links.
## Cookie details
The tracking script sets the following first-party cookies on the merchant's domain:
| Cookie | Purpose | Example value |
| -------------- | ----------------------------------- | ------------------- |
| `agentref_cid` | Click token for conversion matching | `clk_a1b2c3d4e5` |
| `agentref_src` | Traffic source identifier | `script_direct` |
| `agentref_pid` | Program ID | `550e8400-e29b-...` |
| `agentref_vid` | Anonymous visitor ID | `v_k8m2n4p6...` |
| `agentref_ts` | Timestamp of cookie creation | `1711234567890` |
**Key characteristics:**
* **First-party only.** Cookies are set on your domain (e.g., `.yoursite.com`), not a third-party domain. This means they work reliably across browsers, including Safari's ITP restrictions.
* **Configurable duration.** Default is 30 days. You can change this per program in your program settings.
* **Root domain scoping.** Cookies are scoped to your root domain, so they persist across subdomains (e.g., `www.yoursite.com` and `app.yoursite.com`).
* **Secure attributes.** On HTTPS sites, cookies include the `Secure` flag. All cookies use `SameSite=Lax`.
## Attribution logic
AgentRef uses **last-click attribution** – the industry-standard model for affiliate programs. When a payment comes in, AgentRef resolves the affiliate using this priority chain:
| Priority | Method | How it works |
| -------- | --------------- | ------------------------------------------------------------------------------------------------------------- |
| 1 | **Coupon code** | If the customer used a coupon linked to an affiliate, that affiliate gets credit regardless of click history. |
| 2 | **Click token** | The `agentref_cid` from session metadata or `client_reference_id` is matched to the original click record. |
If multiple affiliates drove clicks for the same visitor, the most recent click before conversion wins.
Coupon-based attribution always takes priority over click-based attribution. This means an affiliate with a coupon code gets credit even if the visitor originally arrived through a different affiliate's link.
## How conversions are created
Once a Stripe payment event fires, AgentRef processes it through the webhook pipeline:
Stripe sends `checkout.session.completed` (for new purchases) or `invoice.paid` (for subscription renewals) to AgentRef's Connect webhook endpoint.
AgentRef reads `agentref_cid`, `agentref_pid`, and `agentref_source` from Stripe metadata for custom checkout flows. For hosted Stripe surfaces (`Payment Links`, `Buy Buttons`, `Pricing Tables`), it uses `client_reference_id` as the click-token anchor.
Using the attribution priority chain above, AgentRef identifies which affiliate (if any) drove the sale.
A conversion record is created with status `pending`. The commission amount is calculated based on your program's rate settings.
Automated fraud detection (duplicate clicks, self-referral, geographic anomalies) runs on the conversion. Suspicious conversions are flagged for review.
## Integration points
| Component | What it does | Required? |
| ------------------------------------------------ | ------------------------------------------------ | -------------------------- |
| [Tracking script](/docs/tracking/javascript-snippet) | Captures clicks and sets cookies on your domain | Yes |
| [Stripe Connect](/docs/merchant-guide/connect-stripe) | Sends payment events to AgentRef via webhooks | Yes |
| [Checkout metadata](/docs/tracking/stripe-checkout) | Passes attribution cookies into Stripe sessions | Depends on checkout method |
| [Consent mode](/docs/tracking/consent-gdpr) | Respects GDPR consent before setting cookies | Optional |
| [Server-side tracking](/docs/tracking/server-side) | API-based click recording without the JS snippet | Optional |
| [Debug mode](/docs/tracking/debug-mode) | Inspect tracking state and troubleshoot issues | Optional |
## What's next
Install the tracking script on your site.
Connect tracking to your Stripe checkout flow.
# JavaScript Snippet
Source: https://agentref.co/docs/tracking/javascript-snippet
Install the AgentRef tracking script on your site and configure URL parameters, UTM tracking, sub-IDs, and the DOM API.
The tracking script is a single `
```
Replace `YOUR_PROGRAM_ID` with the UUID from your program's **Tracking** settings page.
Use `next/script` with `strategy="afterInteractive"` so the script loads after hydration without blocking the initial render:
```tsx theme={null}
// app/layout.tsx
import Script from 'next/script';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
Load the script once at your app root. The script is safe to add to a `` or `` via any mechanism – it uses `async` internally and does not block rendering:
```tsx theme={null}
// main.tsx / index.tsx
import { useEffect } from 'react';
function TrackingScript() {
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://www.agentref.co/api/tracking/script.js?pid=YOUR_PROGRAM_ID';
script.async = true;
document.head.appendChild(script);
}, []);
return null;
}
export default TrackingScript;
```
The script is served with a 5-minute CDN cache (`Cache-Control: public, max-age=300`). It is safe to load on every page – the script skips tracking if no referral parameter is present and the visitor has no existing attribution cookies.
## Script attributes
| Attribute | Values | Description |
| -------------- | ---------------- | -------------------------------------------------------------- |
| `data-debug` | `"true"` | Enable debug logging to the browser console on every page load |
| `data-program` | `""` | Override the program ID embedded in the `?pid=` query string |
```html theme={null}
```
You can also trigger debug mode without touching the script tag by adding `?agentref_debug=1` to any URL on your site.
## URL parameters
### Referral parameter (`via`)
Generated AgentRef links use the `via` URL parameter by default:
```
https://yoursite.com?via=abc123
```
Inbound attribution always accepts `via`, `ref`, `r`, and `a`. You can configure additional aliases (for example `aff` or `partner`) in your program's tracking settings. The script checks the default parameters first, then your configured aliases, and uses the first one that has a value.
### UTM parameters
All standard UTM parameters are captured and stored alongside the click record:
| Parameter | Example |
| -------------- | ----------------------------- |
| `utm_source` | `utm_source=newsletter` |
| `utm_medium` | `utm_medium=email` |
| `utm_campaign` | `utm_campaign=spring_launch` |
| `utm_term` | `utm_term=affiliate+software` |
| `utm_content` | `utm_content=cta_button` |
### Ad-click IDs
The script captures ad platform click IDs automatically when present:
| Parameter | Platform |
| ----------- | ------------------- |
| `gclid` | Google Ads |
| `fbclid` | Meta / Facebook Ads |
| `msclkid` | Microsoft Ads |
| `ttclid` | TikTok Ads |
| `li_fat_id` | LinkedIn Ads |
### Sub-IDs
Sub-IDs let affiliates pass custom tracking data through their links for their own attribution needs. Five slots are available:
```
https://yoursite.com?via=abc123&sub1=campaign_a&sub2=email_list&sub3=variant_b
```
Sub-IDs are stored with the click record and appear in conversion data returned to the affiliate via the API.
## Cookies
The script sets the following first-party cookies on your domain:
| Cookie | Purpose | Duration |
| -------------- | -------------------------------------------------- | --------------------------------- |
| `agentref_cid` | Click token – the key for conversion matching | Program setting (default 30 days) |
| `agentref_src` | Traffic source (e.g., `script_direct`, `redirect`) | Program setting (default 30 days) |
| `agentref_pid` | Program ID | Program setting (default 30 days) |
| `agentref_vid` | Anonymous visitor ID (365-day lifetime) | 365 days |
| `agentref_ts` | Timestamp of when attribution was set | Program setting (default 30 days) |
**Cookie characteristics:**
* **First-party only.** Cookies are set on your domain, not `agentref.co`. They are not affected by third-party cookie blocking.
* **Root domain scoping.** Cookies are scoped to `.yoursite.com` so they persist across subdomains (`www`, `app`, `checkout`).
* **`SameSite=Lax`.** Compatible with standard browser security policies. `Secure` flag is added automatically on HTTPS sites.
## DOM API (`window.AgentRef`)
After the script loads, `window.AgentRef` is available with the following methods:
### `AgentRef.ready(callback)`
Run code after tracking has initialized. Use this when you need attribution data at checkout time:
```js theme={null}
window.AgentRef.ready(function() {
const meta = window.AgentRef.getCheckoutMetadata();
if (meta.agentref_cid) {
// Pass to your checkout API
}
});
```
### `AgentRef.getCheckoutMetadata()`
Returns an object to embed in your Stripe checkout session metadata. Returns `{}` if no active attribution is found.
```js theme={null}
window.AgentRef.getCheckoutMetadata();
// {
// agentref_cid: "clk_a1b2c3d4e5",
// agentref_pid: "550e8400-e29b-41d4-a716-446655440000",
// agentref_source: "script_direct"
// }
```
### `AgentRef.getState()`
Returns the current runtime state of the tracker:
```js theme={null}
window.AgentRef.getState();
// {
// ready: true,
// clickToken: "clk_a1b2c3d4e5",
// programId: "550e8400-...",
// visitorId: "v_k8m2n4p6...",
// consentRequired: false,
// consentGranted: true,
// source: "script_direct",
// domainMode: "root"
// }
```
### `AgentRef.refresh()`
Re-run the tracking initialization. Useful after granting cookie consent:
```js theme={null}
AgentRef.setConsent('granted').then(function() {
// Tracking has been re-initialized with consent
});
```
### `AgentRef.setConsent(status)`
Set the user's consent status. Pass `'granted'` to enable tracking or `'denied'` to clear cookies and disable tracking. See [Consent & GDPR](/docs/tracking/consent-gdpr) for full details.
### `AgentRef.getDebugInfo()`
Returns a diagnostic snapshot. See [Debug Mode](/docs/tracking/debug-mode) for details.
# Server-Side Tracking
Source: https://agentref.co/docs/tracking/server-side
Record referral clicks server-side when JavaScript tracking isn't available.
Use server-side tracking when the JavaScript snippet isn't an option, such as mobile apps, server-rendered redirects, or checkout flows where your backend owns the attribution handoff.
Server-side tracking records the **click** and returns a `clickToken`. AgentRef does not expose a public `POST /api/v1/conversions` endpoint. Conversions are created by supported payment integrations, primarily Stripe, when the payment event includes the click token in metadata or `client_reference_id`.
## When to Use
* **Custom payment flows** that don't use Stripe Checkout
* **Mobile apps** or native clients
* **Server-rendered redirects** where you intercept the affiliate link yourself
## Workflow
When a visitor arrives with `?via=AFFILIATE_CODE`, extract the referral code server-side. Inbound attribution also accepts `ref`, `r`, `a`, and configured aliases.
Call `POST /api/tracking/click` with the program ID, page URL, and referral code. The response includes a `clickToken`.
Persist the `clickToken` as `agentref_cid` and the program ID as `agentref_pid` in your session, database, or cookie.
When the visitor converts through Stripe, pass `agentref_cid`, `agentref_pid`, and `agentref_source` into Stripe metadata, or use `client_reference_id` for hosted Stripe surfaces.
## Record a Click
```bash cURL theme={null}
curl -X POST https://www.agentref.co/api/tracking/click \
-H "Content-Type: application/json" \
-H "Origin: https://yourapp.com" \
-d '{
"programId": "550e8400-e29b-41d4-a716-446655440000",
"pageUrl": "https://yourapp.com/?via=john",
"referralCode": "john",
"referrer": "https://blog.example.com/review",
"consentGranted": true
}'
```
```javascript Node.js theme={null}
const response = await fetch('https://www.agentref.co/api/tracking/click', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Must match the configured website domain for the program.
Origin: 'https://yourapp.com',
},
body: JSON.stringify({
programId: '550e8400-e29b-41d4-a716-446655440000',
pageUrl: 'https://yourapp.com/?via=john',
referralCode: 'john',
referrer: 'https://blog.example.com/review',
consentGranted: true,
}),
})
const { clickToken, programId } = await response.json()
// Store clickToken as agentref_cid and programId as agentref_pid.
```
```python Python theme={null}
import requests
resp = requests.post(
"https://www.agentref.co/api/tracking/click",
headers={"Origin": "https://yourapp.com"},
json={
"programId": "550e8400-e29b-41d4-a716-446655440000",
"pageUrl": "https://yourapp.com/?via=john",
"referralCode": "john",
"referrer": "https://blog.example.com/review",
"consentGranted": True,
},
)
click_token = resp.json()["clickToken"]
```
**Response:**
```json theme={null}
{
"status": "ok",
"affiliateCode": "john",
"programId": "550e8400-e29b-41d4-a716-446655440000",
"clickToken": "clk_a1b2c3d4e5",
"cookieDurationDays": 30,
"cookieDomain": "yourapp.com",
"cleanupParams": ["ref"],
"warnings": []
}
```
## Verify Tracking (Optional)
Confirm that a page can verify tracking for a program. This records an install-verification heartbeat; it does not require or accept a click token:
```bash theme={null}
curl -X POST https://www.agentref.co/api/tracking/verify \
-H "Content-Type: application/json" \
-H "Origin: https://yourapp.com" \
-d '{
"programId": "550e8400-e29b-41d4-a716-446655440000",
"pageUrl": "https://yourapp.com/pricing",
"consentGranted": true
}'
```
## Pass Attribution to Stripe
When the visitor converts through Stripe, pass the stored click token into the same metadata fields used by the JavaScript snippet:
```javascript Node.js theme={null}
const agentrefMeta = {
agentref_cid: clickToken,
agentref_pid: programId,
agentref_source: 'server_side',
}
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: [{ price: 'price_123', quantity: 1 }],
success_url: 'https://yourapp.com/success',
cancel_url: 'https://yourapp.com/cancel',
metadata: agentrefMeta,
})
// For subscription checkout sessions, also set:
// subscription_data: { metadata: agentrefMeta }
```
```python Python theme={null}
agentref_meta = {
"agentref_cid": click_token,
"agentref_pid": program_id,
"agentref_source": "server_side",
}
session = stripe.checkout.Session.create(
mode="payment",
line_items=[{"price": "price_123", "quantity": 1}],
success_url="https://yourapp.com/success",
cancel_url="https://yourapp.com/cancel",
metadata=agentref_meta,
)
# For subscription checkout sessions, also set:
# subscription_data={"metadata": agentref_meta}
```
```javascript Hosted Stripe surfaces theme={null}
// For Payment Links, Buy Buttons, and Pricing Tables, use the click token
// as the Stripe client_reference_id when your integration allows it.
const clientReferenceId = clickToken
```
The click and verify endpoints are unauthenticated, but they validate `Origin` or `Referer` against the program's configured website domain. For backend calls, forward or set an origin that matches your configured site.
For more checkout examples, see [Stripe Checkout](/docs/tracking/stripe-checkout).
# Stripe Checkout Integration
Source: https://agentref.co/docs/tracking/stripe-checkout
Pass affiliate attribution into Stripe sessions so AgentRef can match payments to clicks and create commissions automatically.
AgentRef resolves conversions by finding the click token (`agentref_cid`) on a Stripe payment event. How you get that token into Stripe depends on which Stripe surface you use.
## Choose the checkout route first
Before editing code, decide which route applies:
* **Route A — Hosted Stripe on the AgentRef website:** Payment Link, Buy Button, or Pricing Table is on the website where AgentRef is installed.
* **Route B — Website custom checkout:** Buy button is on the AgentRef website and calls your backend to create Stripe Checkout.
* **Route C — External app checkout:** Buy button is in a Chrome extension, desktop app, mobile app, external dashboard, or another surface where AgentRef is not installed.
If Route C applies, do not stop at adding metadata support to the external app request. Route checkout through the AgentRef website first so the website can read the first-party AgentRef cookie before creating the Stripe Checkout Session.
## How `getCheckoutMetadata()` works
The tracking script stores a `agentref_cid` click token in a first-party cookie when a visitor arrives through an affiliate link. At checkout time, your code reads that token and embeds it into the Stripe session before the customer pays.
```js theme={null}
// Returns { agentref_cid, agentref_pid, agentref_source }
// Returns {} if no active attribution exists (visitor came directly)
const meta = window.AgentRef.getCheckoutMetadata();
```
When Stripe fires `checkout.session.completed` or `invoice.paid`, AgentRef's webhook reads the metadata and matches the payment to the original click.
If `getCheckoutMetadata()` returns an empty object, the visitor has no affiliate attribution. You can still call your checkout API – AgentRef will simply not create a conversion for that payment.
## Integration by Stripe surface
This is the most common integration – you create a Checkout Session server-side and redirect the customer to it.
**Step 1:** Read the tracking metadata on your frontend:
```js theme={null}
window.AgentRef.ready(async function() {
const trackingMeta = window.AgentRef.getCheckoutMetadata();
const response = await fetch('/api/create-checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
priceId: 'price_xxx',
agentrefMeta: trackingMeta,
}),
});
const { url } = await response.json();
window.location.href = url;
});
```
**Step 2:** Pass the metadata into the Stripe session on your backend:
```js Node.js theme={null}
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
app.post('/api/create-checkout', async (req, res) => {
const { priceId, agentrefMeta } = req.body;
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: priceId, quantity: 1 }],
success_url: 'https://yoursite.com/success',
cancel_url: 'https://yoursite.com/pricing',
// Pass AgentRef metadata – this is what the webhook reads
metadata: {
...agentrefMeta,
},
});
res.json({ url: session.url });
});
```
```python Python theme={null}
import stripe
import json
from flask import request, jsonify
stripe.api_key = os.environ["STRIPE_SECRET_KEY"]
@app.route("/api/create-checkout", methods=["POST"])
def create_checkout():
data = request.get_json()
price_id = data.get("priceId")
agentref_meta = data.get("agentrefMeta", {})
session = stripe.checkout.Session.create(
mode="subscription",
line_items=[{"price": price_id, "quantity": 1}],
success_url="https://yoursite.com/success",
cancel_url="https://yoursite.com/pricing",
metadata=agentref_meta,
)
return jsonify({"url": session.url})
```
If you use Stripe Payment Links (links to `buy.stripe.com`), the tracking script instruments them **automatically**. No backend changes needed.
When the script detects an active click token, it appends `?client_reference_id=` to any `buy.stripe.com` link on the page using a `MutationObserver`. AgentRef reads this field from the Stripe webhook.
Make sure your tracking script is loaded before the customer can click the payment link. The standard `async` script tag is sufficient – the observer fires on DOM mutations so late-rendered links are also covered.
Automatic Payment Link instrumentation only works when the tracking script is on the same page as the link. If you link to your Stripe Payment Link from a third-party site or email, use [Server-side tracking](/docs/tracking/server-side) instead.
Stripe's embedded `` web component is also instrumented automatically. The script sets the `client-reference-id` attribute on the element when a click token is present.
```html theme={null}
```
The result is equivalent to setting it manually:
```html theme={null}
```
The script uses a `MutationObserver` to catch pricing tables that render after the page loads (e.g., in SPAs). You do not need to wait for the script to load before rendering the pricing table.
If you use Stripe.js or the Payment Intents API directly, read `window.AgentRef.getCheckoutMetadata()` on the client, send it to your server, and embed it into the PaymentIntent or Customer metadata:
```js Node.js theme={null}
app.post('/api/pay', async (req, res) => {
const { agentrefMeta } = req.body;
const paymentIntent = await stripe.paymentIntents.create({
amount: 2900,
currency: 'usd',
metadata: {
...(agentrefMeta || {}),
},
});
res.json({ clientSecret: paymentIntent.client_secret });
});
```
```python Python theme={null}
@app.route("/api/pay", methods=["POST"])
def pay():
data = request.get_json()
metadata = data.get("agentrefMeta", {})
payment_intent = stripe.PaymentIntent.create(
amount=2900,
currency="usd",
metadata=metadata,
)
return jsonify({"clientSecret": payment_intent["client_secret"]})
```
If the buy button lives outside the website where the AgentRef script is installed, route it through your website before sending the user to Stripe.
```txt theme={null}
Chrome extension or external app
-> https://yoursite.com/checkout/start?plan=pro
-> your website reads AgentRef metadata
-> your backend creates Stripe Checkout with that metadata
-> redirect to Stripe
```
The checkout start page should run on the same root domain as your AgentRef script. A typical implementation renders a minimal page, calls `window.AgentRef.getCheckoutMetadata()`, posts the metadata to your existing checkout endpoint, and redirects to the returned Stripe Checkout URL.
If an AI coding agent needs to reroute an existing buy button to this website checkout start page, it should explain the change and ask you before editing. Do not let it silently restructure checkout.
## Verifying attribution
After completing a test purchase, you can confirm attribution was captured correctly by checking the conversion in your AgentRef dashboard. The conversion will appear under **Conversions** with status `pending`.
To verify programmatically, look at the Stripe session's metadata in the Stripe dashboard – you should see `agentref_cid`, `agentref_pid`, and `agentref_source` fields populated for custom checkout flows. For Payment Links, Buy Buttons, and Pricing Tables, verify that Stripe received the click token via `client_reference_id`.
Use [Debug Mode](/docs/tracking/debug-mode) to inspect cookies and confirm `agentref_cid` is set before triggering a test checkout.
## What happens without metadata
If a Stripe payment event arrives without `agentref_cid` in the metadata (and without a `client_reference_id` for Payment Links/Pricing Tables), AgentRef will not create a conversion for that payment. The payment is not affected – only the affiliate attribution is skipped.
Coupon-based attribution does not require metadata. If the customer uses a coupon code linked to an affiliate, a conversion is created regardless of whether tracking cookies were set.
# Webhook Events
Source: https://agentref.co/docs/webhooks/events
All 13 webhook event types with payload examples.
AgentRef sends 13 event types grouped into 5 categories. Subscribe only to the events you need when creating a webhook endpoint.
Marketing Resources lifecycle changes do not currently emit webhook events.
## Envelope Format
Every event is wrapped in the same envelope:
```json theme={null}
{
"id": "msg_abc123",
"type": "conversion.created",
"schemaVersion": 2,
"createdAt": "2026-03-23T14:30:00.000Z",
"merchantId": "mer_abc123",
"programId": "prg_xyz789",
"environment": "production",
"data": {}
}
```
| Field | Type | Description |
| --------------- | --------- | ----------------------------------------- |
| `id` | `string` | Unique message ID (use for idempotency) |
| `type` | `string` | Event type (see below) |
| `schemaVersion` | `2` | Always `2` |
| `createdAt` | `string` | ISO 8601 timestamp |
| `merchantId` | `string` | Your merchant ID |
| `programId` | `string?` | Program ID (if event is program-scoped) |
| `environment` | `string` | `production`, `preview`, or `development` |
| `data` | `object` | Event-specific payload |
***
## Program Events
Fired when a new affiliate program is created.
```json theme={null}
{
"id": "msg_001",
"type": "program.created",
"schemaVersion": 2,
"createdAt": "2026-03-23T14:30:00.000Z",
"merchantId": "mer_abc123",
"programId": "prg_xyz789",
"environment": "production",
"data": {
"id": "prg_xyz789",
"name": "Pro Plan Affiliates",
"commissionType": "recurring",
"commissionPercent": 30,
"cookieDuration": 90,
"status": "active"
}
}
```
Fired when program settings are changed (name, commission, cookie duration, etc.).
```json theme={null}
{
"id": "msg_002",
"type": "program.updated",
"schemaVersion": 2,
"createdAt": "2026-03-23T14:31:00.000Z",
"merchantId": "mer_abc123",
"programId": "prg_xyz789",
"environment": "production",
"data": {
"id": "prg_xyz789",
"name": "Pro Plan Affiliates",
"commissionType": "recurring",
"commissionPercent": 35,
"cookieDuration": 90,
"status": "active",
"updatedFields": ["commissionPercent"]
}
}
```
## Affiliate Events
Fired when an affiliate joins your program (via invite link or marketplace).
```json theme={null}
{
"id": "msg_003",
"type": "affiliate.joined",
"schemaVersion": 2,
"createdAt": "2026-03-23T15:00:00.000Z",
"merchantId": "mer_abc123",
"programId": "prg_xyz789",
"environment": "production",
"data": {
"id": "aff_def456",
"programId": "prg_xyz789",
"email": "affiliate@example.com",
"name": "Jane Smith",
"code": "jane",
"joinSource": "invite",
"isApproved": false,
"isBlocked": false,
"joinedAt": "2026-03-23T15:00:00.000Z",
"approvedAt": null,
"blockedAt": null
}
}
```
Fired when a pending affiliate is approved.
```json theme={null}
{
"id": "msg_004",
"type": "affiliate.approved",
"schemaVersion": 2,
"createdAt": "2026-03-23T15:05:00.000Z",
"merchantId": "mer_abc123",
"programId": "prg_xyz789",
"environment": "production",
"data": {
"id": "aff_def456",
"programId": "prg_xyz789",
"email": "affiliate@example.com",
"name": "Jane Smith",
"code": "jane",
"isApproved": true,
"isBlocked": false,
"approvedAt": "2026-03-23T15:05:00.000Z"
}
}
```
Fired when an affiliate is blocked (manually or by fraud detection).
```json theme={null}
{
"id": "msg_005",
"type": "affiliate.blocked",
"schemaVersion": 2,
"createdAt": "2026-03-23T16:00:00.000Z",
"merchantId": "mer_abc123",
"programId": "prg_xyz789",
"environment": "production",
"data": {
"id": "aff_def456",
"programId": "prg_xyz789",
"email": "affiliate@example.com",
"isApproved": true,
"isBlocked": true,
"blockedReason": "Suspicious click patterns detected",
"blockedAt": "2026-03-23T16:00:00.000Z"
}
}
```
Fired when a previously blocked affiliate is reinstated.
```json theme={null}
{
"id": "msg_006",
"type": "affiliate.unblocked",
"schemaVersion": 2,
"createdAt": "2026-03-23T17:00:00.000Z",
"merchantId": "mer_abc123",
"programId": "prg_xyz789",
"environment": "production",
"data": {
"id": "aff_def456",
"programId": "prg_xyz789",
"email": "affiliate@example.com",
"isApproved": true,
"isBlocked": false,
"blockedReason": null,
"blockedAt": null,
"updatedAt": "2026-03-23T17:00:00.000Z"
}
}
```
## Conversion Events
Fired when a new conversion is recorded from a supported attribution source such as Stripe metadata, Stripe `client_reference_id`, or coupon attribution.
```json theme={null}
{
"id": "msg_007",
"type": "conversion.created",
"schemaVersion": 2,
"createdAt": "2026-03-23T18:00:00.000Z",
"merchantId": "mer_abc123",
"programId": "prg_xyz789",
"environment": "production",
"data": {
"id": "conv_ghi789",
"affiliateId": "aff_def456",
"programId": "prg_xyz789",
"clickId": "clk_abc123",
"paymentProvider": "stripe",
"externalId": "cs_live_abc123",
"saleAmount": 9900,
"refundedAmount": 0,
"currency": "usd",
"isRecurring": false,
"recurringMonth": null,
"status": "pending",
"attributionMethod": "click_token",
"createdAt": "2026-03-23T18:00:00.000Z"
}
}
```
Fired when a conversion is refunded (triggered by Stripe refund or manual action).
```json theme={null}
{
"id": "msg_008",
"type": "conversion.refunded",
"schemaVersion": 2,
"createdAt": "2026-03-23T19:00:00.000Z",
"merchantId": "mer_abc123",
"programId": "prg_xyz789",
"environment": "production",
"data": {
"id": "conv_ghi789",
"affiliateId": "aff_def456",
"programId": "prg_xyz789",
"paymentProvider": "stripe",
"externalId": "ch_abc123",
"saleAmount": 9900,
"refundedAmount": 9900,
"currency": "usd",
"status": "refunded",
"updatedAt": "2026-03-23T19:00:00.000Z"
}
}
```
## Payout Events
Fired when a payout is created for an affiliate.
```json theme={null}
{
"id": "msg_009",
"type": "payout.created",
"schemaVersion": 2,
"createdAt": "2026-03-24T10:00:00.000Z",
"merchantId": "mer_abc123",
"environment": "production",
"data": {
"id": "pay_jkl012",
"affiliateId": "aff_def456",
"programId": "prg_xyz789",
"amount": 15000,
"feeAmount": 0,
"netAmount": 15000,
"currency": "usd",
"method": "paypal",
"status": "pending",
"periodStart": "2026-03-01T00:00:00.000Z",
"periodEnd": "2026-03-31T23:59:59.000Z",
"createdAt": "2026-03-24T10:00:00.000Z"
}
}
```
Fired when a payout moves into the manual processing step after the merchant starts the external payment.
```json theme={null}
{
"id": "msg_010",
"type": "payout.processing",
"schemaVersion": 2,
"createdAt": "2026-03-24T10:05:00.000Z",
"merchantId": "mer_abc123",
"environment": "production",
"data": {
"id": "pay_jkl012",
"affiliateId": "aff_def456",
"programId": "prg_xyz789",
"amount": 15000,
"netAmount": 15000,
"currency": "usd",
"method": "paypal",
"status": "processing"
}
}
```
Fired when a payout is marked completed after the external payment succeeds.
```json theme={null}
{
"id": "msg_011",
"type": "payout.completed",
"schemaVersion": 2,
"createdAt": "2026-03-24T12:00:00.000Z",
"merchantId": "mer_abc123",
"environment": "production",
"data": {
"id": "pay_jkl012",
"affiliateId": "aff_def456",
"programId": "prg_xyz789",
"amount": 15000,
"netAmount": 15000,
"currency": "usd",
"method": "paypal",
"status": "completed",
"processedAt": "2026-03-24T10:05:00.000Z",
"completedAt": "2026-03-24T12:00:00.000Z"
}
}
```
Fired when a manual payout attempt fails and the payout is marked for retry or follow-up.
```json theme={null}
{
"id": "msg_012",
"type": "payout.failed",
"schemaVersion": 2,
"createdAt": "2026-03-24T12:00:00.000Z",
"merchantId": "mer_abc123",
"environment": "production",
"data": {
"id": "pay_jkl012",
"affiliateId": "aff_def456",
"programId": "prg_xyz789",
"amount": 15000,
"netAmount": 15000,
"currency": "usd",
"method": "paypal",
"status": "failed",
"failureReason": "External payment rejected by provider"
}
}
```
## Fraud Events
Fired when a fraud flag is resolved (approved or rejected).
```json theme={null}
{
"id": "msg_013",
"type": "flag.resolved",
"schemaVersion": 2,
"createdAt": "2026-03-24T14:00:00.000Z",
"merchantId": "mer_abc123",
"programId": "prg_xyz789",
"environment": "production",
"data": {
"id": "flag_mno345",
"affiliateId": "aff_def456",
"type": "suspicious_click_pattern",
"resolution": "dismissed",
"resolvedAt": "2026-03-24T14:00:00.000Z",
"resolvedBy": "merchant"
}
}
```
## Quick Reference
| Event | Category | When |
| --------------------- | ---------- | ------------------------- |
| `program.created` | Program | New program created |
| `program.updated` | Program | Program settings changed |
| `affiliate.joined` | Affiliate | Affiliate joins program |
| `affiliate.approved` | Affiliate | Affiliate approved |
| `affiliate.blocked` | Affiliate | Affiliate blocked |
| `affiliate.unblocked` | Affiliate | Affiliate reinstated |
| `conversion.created` | Conversion | New conversion recorded |
| `conversion.refunded` | Conversion | Conversion refunded |
| `payout.created` | Payout | Payout created |
| `payout.processing` | Payout | Payout processing started |
| `payout.completed` | Payout | Payout marked completed |
| `payout.failed` | Payout | Payout failed |
| `flag.resolved` | Fraud | Fraud flag resolved |
# Webhooks Overview
Source: https://agentref.co/docs/webhooks/overview
Get notified in real time when affiliate, conversion, and payout events happen in your programs.
Webhooks let your backend react to AgentRef events without polling the API. When something interesting happens – an affiliate joins, a conversion is created, a payout completes – AgentRef sends an HTTP POST to your endpoint with a signed JSON payload.
## How it works
1. You register an HTTPS endpoint (your server URL)
2. You choose which event types to subscribe to
3. AgentRef sends a signed `POST` request to your URL every time a matching event fires
4. Your server responds with a `2xx` status to acknowledge receipt
5. If delivery fails, AgentRef retries automatically on an exponential backoff schedule
## Webhook envelope
Every webhook delivery shares the same outer envelope regardless of event type:
```json theme={null}
{
"id": "msg_01jq2k3n4p5q6r7s8t9u0v1w2x",
"type": "conversion.created",
"schemaVersion": 2,
"createdAt": "2026-03-15T14:22:00.000Z",
"merchantId": "mch_01jq2abc",
"programId": "prg_01jq2def",
"environment": "production",
"data": {
"id": "conv_01jq2ghi",
"affiliateId": "aff_01jq2jkl",
"saleAmount": 9900,
"currency": "usd",
"status": "pending"
}
}
```
| Field | Description |
| --------------- | -------------------------------------------------------------------- |
| `id` | Unique message ID. Use this for idempotency. |
| `type` | Event type string, e.g. `conversion.created` |
| `schemaVersion` | Always `2` for current payloads |
| `createdAt` | ISO 8601 timestamp when the event was created |
| `merchantId` | Your merchant ID |
| `programId` | Program ID the event belongs to (omitted for merchant-scoped events) |
| `environment` | `production`, `preview`, or `development` |
| `data` | Event-specific payload (see [Events](/docs/webhooks/events)) |
## Registering a webhook endpoint
1. Go to **Settings → Webhooks** in your dashboard
2. Click **Add endpoint**
3. Enter your endpoint URL and a descriptive name
4. Select the event types you want to receive
5. Optionally scope the endpoint to a specific program
6. Click **Create** – your signing secret is shown once; copy it now
```bash cURL theme={null}
curl -X POST https://www.agentref.co/api/v1/webhooks \
-H "Authorization: Bearer ak_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Production webhook",
"url": "https://your-app.com/webhooks/agentref",
"subscribedEvents": [
"conversion.created",
"payout.completed"
]
}'
```
```typescript Node.js theme={null}
import { AgentRef } from 'agentref';
const client = new AgentRef({ apiKey: 'ak_live_...' });
const { endpoint, signingSecret } = await client.webhooks.create({
name: 'Production webhook',
url: 'https://your-app.com/webhooks/agentref',
subscribedEvents: ['conversion.created', 'payout.completed'],
});
// Store signingSecret securely – it is only returned once
console.log(signingSecret);
```
```python Python theme={null}
from agentref import AgentRef
client = AgentRef(api_key='ak_live_...')
result = client.webhooks.create(
name='Production webhook',
url='https://your-app.com/webhooks/agentref',
subscribed_events=['conversion.created', 'payout.completed'],
)
# Store result.signing_secret securely – it is only returned once
print(result.signing_secret)
```
The signing secret is only returned in the creation response. Store it in your secrets manager immediately. You cannot retrieve it again – only rotate it.
The response includes:
```json theme={null}
{
"data": {
"endpoint": {
"id": "wh_01jq2k3n...",
"name": "Production webhook",
"url": "https://your-app.com/webhooks/agentref",
"status": "active",
"programId": null,
"schemaVersion": 2,
"subscribedEvents": ["conversion.created", "payout.completed"],
"secretLastFour": "a3f9",
"createdAt": "2026-03-15T14:00:00.000Z",
"updatedAt": "2026-03-15T14:00:00.000Z",
"disabledAt": null
},
"signingSecret": "whsec_..."
}
}
```
## Event type filtering
You must subscribe to at least one event type per endpoint. You can subscribe to any subset of the [13 available events](/docs/webhooks/events). Only events in your subscription list will be delivered to that endpoint.
Marketing Resources lifecycle changes do not currently emit webhook events.
To update subscriptions on an existing endpoint:
```bash cURL theme={null}
curl -X PATCH https://www.agentref.co/api/v1/webhooks/wh_01jq2k3n \
-H "Authorization: Bearer ak_live_..." \
-H "Content-Type: application/json" \
-d '{"subscribedEvents": ["conversion.created", "conversion.refunded", "payout.completed"]}'
```
## Program scoping
By default, a webhook endpoint receives events from **all** your programs. If you have multiple programs and want to isolate events, you can scope an endpoint to a specific program by passing `programId` when creating or updating the endpoint.
A scoped endpoint only receives events where `programId` matches. Unscoped endpoints receive events from every program.
```bash cURL theme={null}
curl -X POST https://www.agentref.co/api/v1/webhooks \
-H "Authorization: Bearer ak_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Program A only",
"url": "https://your-app.com/webhooks/program-a",
"subscribedEvents": ["affiliate.joined"],
"programId": "prg_01jq2def"
}'
```
## Endpoint requirements
* **HTTPS only** – HTTP endpoints are rejected in production (localhost HTTP is allowed during development)
* **Respond within 30 seconds** – requests that time out are treated as failed deliveries
* **Return a 2xx status** – any non-2xx response triggers a retry, including 3xx redirects
* **No authentication required on your side** – verify the request using the [signature](/docs/webhooks/signature-verification) instead
## Managing endpoints via API
| Method | Path | Scope required | Description |
| -------- | ------------------------------------- | ---------------- | ---------------------------------- |
| `GET` | `/api/v1/webhooks` | `webhooks:read` | List all endpoints |
| `POST` | `/api/v1/webhooks` | `webhooks:write` | Create an endpoint |
| `GET` | `/api/v1/webhooks/{id}` | `webhooks:read` | Get a single endpoint |
| `PATCH` | `/api/v1/webhooks/{id}` | `webhooks:write` | Update name, URL, or subscriptions |
| `DELETE` | `/api/v1/webhooks/{id}` | `webhooks:write` | Disable an endpoint |
| `POST` | `/api/v1/webhooks/{id}/rotate-secret` | `webhooks:write` | Rotate the signing secret |
Deleting an endpoint marks it as `disabled`. Disabled endpoints stop receiving deliveries and cannot be re-enabled via API – create a new endpoint instead.
## Next steps
Verify incoming requests are genuinely from AgentRef
Full payload examples for all 13 event types
Understand retries, idempotency, and failure handling
Test your endpoint locally before going to production
# Retry & Delivery
Source: https://agentref.co/docs/webhooks/retry-delivery
How AgentRef retries failed webhook deliveries and how to handle them reliably.
AgentRef automatically retries failed webhook deliveries with exponential backoff. Here's how to build a reliable webhook consumer.
## Retry Schedule
If your endpoint doesn't respond with a `2xx` status code within 30 seconds, AgentRef retries:
| Attempt | Delay | Cumulative Time |
| ------- | ---------- | --------------- |
| 1 | Immediate | 0 |
| 2 | 1 minute | 1 min |
| 3 | 5 minutes | 6 min |
| 4 | 15 minutes | 21 min |
| 5 | 1 hour | \~1.3 hours |
| 6 | 3 hours | \~4.3 hours |
| 7 | 6 hours | \~10.3 hours |
| 8 | 12 hours | \~22.3 hours |
| 9 | 24 hours | \~46.3 hours |
After all 8 retry attempts are exhausted (9 total including the first), the message is marked as failed.
## What Counts as Failure
| Scenario | Retried? |
| ------------------------------------- | ------------ |
| Non-2xx HTTP response (3xx, 4xx, 5xx) | Yes |
| Connection timeout (>30 seconds) | Yes |
| DNS resolution failure | Yes |
| TLS/SSL error | Yes |
| 2xx response | No (success) |
## Handling Duplicates
Your endpoint may receive the same event more than once – either from retries or from concurrent deliveries. Use the `id` field in the webhook envelope for idempotency:
```javascript theme={null}
const processedEvents = new Set() // Use a database in production
app.post('/webhooks/agentref', (req, res) => {
const event = req.body
// Skip if already processed
if (processedEvents.has(event.id)) {
return res.status(200).send('Already processed')
}
// Process the event
handleEvent(event)
processedEvents.add(event.id)
res.status(200).send('OK')
})
```
In production, store processed event IDs in your database with a unique constraint rather than an in-memory Set.
## Best Practices
### Respond Quickly
Return a `200` response as soon as you receive the webhook. Process the event asynchronously:
```javascript theme={null}
app.post('/webhooks/agentref', (req, res) => {
// Respond immediately
res.status(200).send('OK')
// Process asynchronously
processEventAsync(req.body).catch(console.error)
})
```
### Use a Queue
For complex processing, push webhook events to a queue (Redis, SQS, etc.) and process them with a worker:
```javascript theme={null}
app.post('/webhooks/agentref', async (req, res) => {
await queue.push('webhook-events', req.body)
res.status(200).send('OK')
})
```
### Monitor Endpoint Health
If your endpoint consistently fails, webhook deliveries will accumulate retries. Monitor your webhook endpoint's response times and error rates.
After all retries are exhausted, failed messages are **not** automatically re-sent. Check your webhook logs in the AgentRef dashboard to identify and investigate persistent failures.
# Signature Verification
Source: https://agentref.co/docs/webhooks/signature-verification
Verify webhook signatures to ensure payloads are authentically from AgentRef.
Every webhook delivery is signed with HMAC-SHA256. Verify the signature to confirm the payload came from AgentRef and wasn't tampered with.
The Node.js and Python SDKs do not currently expose a webhook signature helper. Use the manual verification algorithm below, or wrap it in your own helper for your framework.
## Signature Format
AgentRef sends three headers with each webhook delivery:
| Header | Description |
| ---------------- | -------------------------------------------------- |
| `svix-id` | Unique message ID |
| `svix-timestamp` | Unix timestamp (seconds) when the message was sent |
| `svix-signature` | `v1,{base64_signature}` |
## Verification Algorithm
Read the body as a raw string – do **not** parse JSON first. Parsing and re-serializing can change whitespace or key order, breaking the signature.
Extract `svix-id`, `svix-timestamp`, and `svix-signature` from the request headers.
Build the signed content string: `{svix-id}.{svix-timestamp}.{body}`
Compute HMAC-SHA256 of this string using your webhook signing secret (the part after `whsec_`, base64url-decoded).
Base64-encode your HMAC result and compare it to the signature value (after the `v1,` prefix) using constant-time comparison.
## Code Examples
```javascript Node.js (Express) theme={null}
import crypto from 'crypto'
function verifyWebhookSignature(req, secret) {
const msgId = req.headers['svix-id']
const timestamp = req.headers['svix-timestamp']
const signature = req.headers['svix-signature']
// Check timestamp to prevent replay attacks (5 min tolerance)
const now = Math.floor(Date.now() / 1000)
if (Math.abs(now - parseInt(timestamp)) > 300) {
throw new Error('Timestamp too old')
}
// Decode the secret (remove whsec_ prefix, base64url decode)
const secretBytes = Buffer.from(secret.replace('whsec_', ''), 'base64url')
// Build the signed content
const signedContent = `${msgId}.${timestamp}.${req.body}`
// Compute expected signature
const expected = crypto
.createHmac('sha256', secretBytes)
.update(signedContent)
.digest('base64')
// Extract actual signature (remove v1, prefix)
const actual = signature.split(',')[1]
// Constant-time comparison
const expectedBytes = Buffer.from(expected)
const actualBytes = Buffer.from(actual)
if (expectedBytes.length !== actualBytes.length || !crypto.timingSafeEqual(expectedBytes, actualBytes)) {
throw new Error('Invalid signature')
}
return JSON.parse(req.body)
}
// Express route
app.post('/webhooks/agentref', express.raw({ type: 'application/json' }), (req, res) => {
try {
const event = verifyWebhookSignature(req, process.env.AGENTREF_WEBHOOK_SECRET)
console.log('Verified event:', event.type)
res.status(200).send('OK')
} catch (err) {
console.error('Verification failed:', err.message)
res.status(400).send('Invalid signature')
}
})
```
```python Python (Flask) theme={null}
import hmac
import hashlib
import base64
import json
import os
import time
from flask import Flask, request
app = Flask(__name__)
def verify_webhook_signature(payload, headers, secret):
msg_id = headers.get("svix-id")
timestamp = headers.get("svix-timestamp")
signature = headers.get("svix-signature")
# Check timestamp (5 min tolerance)
now = int(time.time())
if abs(now - int(timestamp)) > 300:
raise ValueError("Timestamp too old")
# Decode secret (remove whsec_ prefix, base64url decode)
secret_bytes = base64.urlsafe_b64decode(secret.removeprefix("whsec_") + "==")
# Build signed content
signed_content = f"{msg_id}.{timestamp}.{payload}"
# Compute expected signature
expected = base64.b64encode(
hmac.new(secret_bytes, signed_content.encode(), hashlib.sha256).digest()
).decode()
# Extract actual (remove v1, prefix)
actual = signature.split(",")[1]
if not hmac.compare_digest(expected, actual):
raise ValueError("Invalid signature")
return json.loads(payload)
@app.route("/webhooks/agentref", methods=["POST"])
def webhook():
try:
event = verify_webhook_signature(
request.get_data(as_text=True),
request.headers,
os.environ["AGENTREF_WEBHOOK_SECRET"],
)
print(f"Verified event: {event['type']}")
return "OK", 200
except ValueError as e:
return str(e), 400
```
## Common Pitfalls
**Don't parse the body before verifying.** If your framework automatically parses JSON, use a raw body parser for the webhook route. `JSON.parse(JSON.stringify(body))` may reorder keys or change whitespace, producing a different signature.
* **Use `express.raw()` in Express** (not `express.json()`) for the webhook route
* **In Next.js App Router**, use `await request.text()` to get the raw body
* **Always use constant-time comparison** (`crypto.timingSafeEqual` in Node, `hmac.compare_digest` in Python) to prevent timing attacks
* **Validate the timestamp** – reject events older than 5 minutes to prevent replay attacks
# Testing Webhooks
Source: https://agentref.co/docs/webhooks/testing
How to test webhook delivery and signature verification locally.
Test your webhook integration before going to production.
## Testing Locally with ngrok
Your webhook endpoint needs a public URL. Use [ngrok](https://ngrok.com) to tunnel to your local server:
```bash theme={null}
node server.js
# Listening on http://localhost:3000
```
```bash theme={null}
ngrok http 3000
# Forwarding: https://abc123.ngrok.io → http://localhost:3000
```
Use the ngrok URL as your webhook endpoint:
```bash theme={null}
curl -X POST https://www.agentref.co/api/v1/webhooks \
-H "Authorization: Bearer ak_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Local Testing",
"url": "https://abc123.ngrok.io/webhooks/agentref",
"subscribedEvents": ["conversion.created", "affiliate.joined"]
}'
```
Perform an action that triggers a webhook, such as completing a Stripe test checkout with attribution metadata or approving an affiliate application.
## Simulating a Webhook with cURL
To test your endpoint's signature verification, simulate a delivery:
```bash theme={null}
# Your webhook secret
SECRET="whsec_your_secret_here"
# Build the payload
BODY='{"id":"msg_test_001","type":"conversion.created","schemaVersion":2,"createdAt":"2026-03-23T18:00:00.000Z","merchantId":"mer_test","environment":"development","data":{"id":"conv_test","affiliateId":"aff_test","programId":"prg_test","paymentProvider":"stripe","externalId":"cs_test","saleAmount":9900,"refundedAmount":0,"currency":"usd","status":"pending"}}'
# Generate signature
MSG_ID="msg_test_001"
TIMESTAMP=$(date +%s)
SIGNATURE=$(echo -n "${MSG_ID}.${TIMESTAMP}.${BODY}" | openssl dgst -sha256 -hmac $(echo -n "${SECRET#whsec_}" | base64 -d) -binary | base64)
# Send the request
curl -X POST http://localhost:3000/webhooks/agentref \
-H "Content-Type: application/json" \
-H "svix-id: ${MSG_ID}" \
-H "svix-timestamp: ${TIMESTAMP}" \
-H "svix-signature: v1,${SIGNATURE}" \
-d "${BODY}"
```
## Verification Checklist
Before going to production, confirm:
* [ ] Your endpoint returns `200` within 30 seconds
* [ ] Signature verification works correctly
* [ ] You handle duplicate deliveries (check event `id`)
* [ ] You process events asynchronously (don't block the response)
* [ ] Your endpoint uses HTTPS in production
* [ ] You've subscribed only to events you need
* [ ] Error handling is in place – a crash won't take down your server
## Debugging Delivery Issues
If webhooks aren't arriving:
1. **Check the dashboard** – go to Settings → Webhooks to see delivery status and errors
2. **Verify the URL** – is your endpoint reachable from the public internet?
3. **Check HTTPS** – production webhooks require HTTPS (localhost HTTP is allowed in development)
4. **Check event subscription** – did you subscribe to the event types you're expecting?
5. **Check response code** – a non-2xx response triggers retries, not immediate re-delivery