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

# 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.

<CodeGroup>
  ```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",
  )
  ```
</CodeGroup>

***

## 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.

<CodeGroup>
  ```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}",
                  )
  ```
</CodeGroup>

***

## 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.

<CodeGroup>
  ```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)}")
  ```
</CodeGroup>

***

## 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:

<CodeGroup>
  ```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"
  ```
</CodeGroup>

### Retry-Safe Workflows

When chaining multiple operations, use a unique key for each step:

<CodeGroup>
  ```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}".
  ```
</CodeGroup>

***

## Error Handling Patterns

### Graceful Degradation

When an agent encounters an error, it should attempt recovery before giving up:

<CodeGroup>
  ```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,
          }
  ```
</CodeGroup>

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