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

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

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

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

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

The async client supports `async with` for automatic connection cleanup. All async resource methods are identical to their sync counterparts but return coroutines.
