# Fanful API reference

Fanful's public machine contract is available today through HTTP agent manifests, the public MCP endpoint, action contracts, workflow triggers, sync feeds, and the repo CLI. This is not yet a fully productized Stripe-style public REST API with official language SDK packages, API keys, changeloged resource versions, and generated clients. The current API reference documents what is executable now and what must ship before official SDK release.

## Status

- Public MCP endpoint: https://fanful.net/mcp
- API discovery index: https://fanful.net/llms.txt
- Agent setup: https://fanful.net/agent-setup
- API markdown: https://docs.fanful.net/api.md
- Current official SDK packages: not published yet
- Current starter examples: https://fanful.net/agent-docs/fanful-agent-sdk-starter.md
- Current CLI: repo-local `npm --silent run fanful:cli -- <command>`

## Core resources

- [Surface catalog](https://fanful.net/api/agent/action-contracts): Use before any mutation to discover safe confirmation and redaction rules.
- [Community chat](https://fanful.net/api/agent/community-chat): Read visible channels/messages and post confirmed listener messages through MCP.
- [Creator commerce](https://fanful.net/api/agent/creator-commerce): Creator lesson, shop, membership, Stripe Price, and community-channel contracts.
- [Live controls](https://fanful.net/api/agent/live-controls): Schedule, update, preview, and status-change live events with confirmed wrappers.
- [Listener experience](https://fanful.net/api/agent/listener-experience): Listener entitlements, profile/preferences, confirmed display-name writes, support checkout, purchases, and Listen Along boundaries.
- [Workflow triggers](https://fanful.net/api/agent/workflow-triggers): Webhook-style trigger families, samples, signatures, subscriptions, and delivery status.
- [Workflow trigger subscriptions](https://fanful.net/api/agent/workflow-triggers/subscriptions): Authenticated registry for active external webhook endpoints and manual signed test deliveries.
- [Agent sessions](https://fanful.net/api/agent/sessions): Redacted run/session contract and sample event replay for future durable agent runs.
- [Sync jobs](https://fanful.net/api/agent/sync-jobs): Durable redacted checkpoints for catalog/import sync work.
- [Work-log sync feed](https://fanful.net/api/agent/sync-feeds/work-log): Public cursor feed of Fanful work-log updates for external agents.

## Auth and safety

- Action contracts are Fanful's safety manifest, not a separate public protocol. They describe available actions, required auth/scopes, confirmation rules, idempotency, audit, stale-state, and redaction policy.
- Public action-contract manifests must stay metadata-only: no bearer tokens, signing secrets, raw listener rows, provider secret ids, signed media URLs, or private dashboard data.
- Guest reads are public only when the manifest says so.
- Private listener and creator access uses first-party sessions or scoped delegated bearer grants.
- Admin/operator fallback credentials stay in environment variables or transport headers, never prompt-visible tool input.
- Mutations use `agent-write-envelope.v1` with exact confirmation, idempotency, reason, audit correlation, actor attribution, client attribution, and stale-state guards when required.
- Workflow triggers are signed, redacted event notifications. Verify `x-fanful-signature`, use idempotency keys, and read linked manifests for current state before acting.

## Code examples

### Read the action-contract manifest

Start here before any write. It tells agents which operations are executable, preview-only, or blocked.

```bash
curl -fsS https://fanful.net/api/agent/action-contracts \
  -H 'accept: application/json'
```

### Validate a write envelope before calling a tool

Confirmed writes use an envelope with actor attribution, client attribution, exact confirmation, idempotency, reason, and audit correlation.

```bash
curl -fsS https://fanful.net/api/agent/action-contracts/envelope \
  -H 'content-type: application/json' \
  -d '{
    "action": {
      "requested": "fanful_creator_live_schedule_create",
      "risk": "audience-visible"
    },
    "actor": { "role": "artist-admin" },
    "client": { "kind": "agent", "name": "Example integration" },
    "target": {
      "type": "live_event",
      "summary": "Laurel live at 10:30pm Pacific"
    },
    "confirmation": {
      "text": "I confirm creator.live.schedule-and-status for Laurel live at 10:30pm Pacific."
    },
    "idempotencyKey": "example-live-2026-05-20-1030pm",
    "auditCorrelationId": "example-audit-live-1030pm",
    "reason": "Artist asked the agent to schedule and notify followers."
  }'
```

### Subscribe an external endpoint to Fanful events

Workflow-trigger subscription writes require admin/scoped authorization. Keep the bearer token and signing secret out of prompts.

```bash
curl -fsS https://fanful.net/api/agent/workflow-triggers/subscriptions \
  -H 'authorization: Bearer $FANFUL_AGENT_BEARER_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
    "action": "create_subscription",
    "endpointUrl": "https://example.com/fanful/webhooks",
    "triggerFamilies": ["member.idea.submitted"],
    "signingSecretReference": "secret:fanful-webhook-production",
    "description": "Route new member ideas into our moderation queue."
  }'
```

### Verify a Fanful webhook signature in Python

Use the raw request body, timestamp header, and shared signing secret. Do not ask an AI agent to handle the raw secret in a prompt.

```python
import hashlib
import hmac
import time

def verify_fanful_signature(raw_body: bytes, headers: dict[str, str], secret: str) -> bool:
    timestamp = headers.get("x-fanful-timestamp", "")
    signature = headers.get("x-fanful-signature", "")
    if not timestamp or not signature:
        return False
    if abs(time.time() - int(timestamp)) > 300:
        return False
    signed = timestamp.encode("utf-8") + b"." + raw_body
    expected = "sha256=" + hmac.new(secret.encode("utf-8"), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)
```

### Subscriber-to-Mailchimp-style recipe

When Fanful emits a signed subscriber/member event, verify the webhook, read the linked manifest for current state, then update your own platform. Replace the Mailchimp URL with the provider you use.

```ts
export async function handleFanfulWebhook(request: Request) {
  const rawBody = await request.text();
  const ok = await verifyFanfulWebhook(rawBody, request.headers, process.env.FANFUL_WEBHOOK_SECRET!);
  if (!ok) return new Response("invalid signature", { status: 401 });

  const event = JSON.parse(rawBody);
  if (event.type !== "member.idea.submitted" && event.type !== "listener.support-checkout.completed") {
    return Response.json({ ignored: true });
  }

  // Read the current Fanful manifest before taking action; webhook payloads are redacted notifications.
  await fetch(event.links.currentManifest, { headers: { accept: "application/json" } });

  await fetch("https://usX.api.mailchimp.com/3.0/lists/LIST_ID/members", {
    method: "POST",
    headers: {
      authorization: `Bearer ${process.env.MAILCHIMP_API_KEY}`,
      "content-type": "application/json",
    },
    body: JSON.stringify({
      email_address: event.actor?.emailHash ? undefined : event.actor?.email,
      status_if_new: "subscribed",
      tags: ["fanful"],
    }),
  });

  return Response.json({ ok: true, fanfulEventId: event.id });
}
```

## Official language SDK release plan

Before Fanful publishes official SDK packages such as `fanful-python` or `@fanful/sdk`, ship these pieces:

1. Versioned public API policy: stable endpoint taxonomy, backwards-compatibility policy, changelog, deprecation window, and typed error catalog.
2. Auth productization: user-created API keys or OAuth/scoped grants for non-agent apps, key rotation, revocation, scope docs, and rate limits.
3. OpenAPI or generated schema source: machine-readable schemas for manifests, action contracts, workflow triggers, sync feeds, and confirmed writes.
4. SDK core behavior: retry policy, idempotency helpers, pagination/cursor helpers, webhook signature verification, redaction-safe logging, and typed errors.
5. Examples: read manifests, start MCP, validate a write envelope, create a workflow-trigger subscription, verify a webhook, and sync a subscriber/member event into another platform.
6. Packaging and CI: package ownership, signing/release automation, semantic versioning, matrix tests, docs build, smoke tests against fixtures and production read-only endpoints.
7. Support policy: security contact, issue templates, example app maintenance, and explicit not-ready notes for unsupported writes.

## Common recipes

- Give an agent one URL: send it https://fanful.net/agent-setup/prompt.md.
- Read latest community messages: call `fanful_listener_community_messages_read` with `{"channel":"#general","limit":10}`.
- Post a community reply: call `fanful_listener_community_message_post` only with scoped/session listener auth and exact public-message confirmation.
- Update a listener display name: read `listener.profilePreferences.profile.displayName`; for ChatGPT UI, optionally render `fanful_listener_display_name_confirmation_render`; then call `fanful_listener_display_name_update` with the new name, observed display name, exact `I confirm listener.profile.display-name.update for display name "<name>".` confirmation, acknowledged risk, idempotency, reason, and audit correlation.
- Start listener checkout: preview support, membership, or signed-CD shop terms first; for ChatGPT UI, render `fanful_listener_checkout_confirmation_render`; then call `fanful_listener_support_checkout_start`, `fanful_listener_membership_checkout_start`, or `fanful_listener_shop_checkout_start` with the exact preview confirmation text, acknowledged risk, idempotency, reason, and audit correlation. Lesson and paid-room checkout starts remain blocked.
- Read lesson-credit ledger health: call `fanful_creator_lesson_credit_ledger_read` with creator/admin authorization. Use aggregate totals, event-type breakdowns, blocked write identifiers, and recent event markers only; raw student email, user id, booking/purchase ids, Stripe ids, idempotency keys, reasons, and metadata are intentionally unavailable.
- Update lesson price or policy settings: in ChatGPT UI, optionally render `fanful_creator_lesson_price_policy_confirmation_render`; then call `fanful_creator_service_price_policy_update` or `fanful_creator_lesson_policy_update` with exact confirmation, stale-state guard, idempotency, reason, and audit correlation.
- Update lesson availability: in ChatGPT UI, optionally render `fanful_creator_lesson_availability_confirmation_render`; then call `fanful_creator_lesson_availability_update` for one future open/cancelled window with exact confirmation, stale-state guard, idempotency, reason, and audit correlation.
- Create a VIP tier: in ChatGPT UI, optionally render `fanful_creator_membership_tier_confirmation_render`; create the inactive tier draft, create/select the membership Stripe Price, then activate/restore the tier only through confirmed creator membership tools.
- Gate a channel to a tier: for ChatGPT UI, optionally render `fanful_creator_entitlement_metadata_confirmation_render`; then use `fanful_creator_community_channel_create` or `fanful_creator_community_channel_update` with `accessMode:"membership-tiers"` and active `tierIds`, or `fanful_creator_entitlement_metadata_update` for focused existing-channel access metadata changes.
- Create a shop product safely: in ChatGPT UI, optionally render `fanful_creator_shop_product_confirmation_render`; then use `fanful_creator_shop_product_create` after re-reading creator commerce state. It inserts catalog metadata only and does not set raw download URLs, external URLs, Stripe Price ids, checkout sessions, fulfillment execution, orders, refunds, or credits.
- Update an existing shop product safely: in ChatGPT UI, optionally render `fanful_creator_shop_product_confirmation_render`; then use `fanful_creator_shop_product_visibility_update` or `fanful_creator_shop_product_details_update` after re-reading creator commerce state. Details can update copy, inventory, fulfillment note, download display metadata, and sort order, but not raw download URLs, external URLs, product kind/id, checkout, order fulfillment, or refunds.
- Change an existing shop product Price: in ChatGPT UI, optionally render `fanful_creator_shop_product_confirmation_render`; then use `fanful_creator_shop_product_stripe_price_select` to select/clear a compatible app-state Price, or `fanful_creator_shop_product_stripe_price_create` to create one replacement one-time Price after a fresh catalog read, exact confirmation, idempotency, stale-state, and audit correlation.
- Change live status: in ChatGPT UI, optionally render `fanful_creator_live_status_confirmation_render`; then use `fanful_creator_live_status_update` after `fanful_live_status_preview` with exact confirmation, observed `live_events.updated_at`, idempotency, reason, and audit correlation. New event creation, schedule edits, stream provider provisioning, roster/stage changes, chat moderation, and notification fan-out use separate tools.
- Schedule a live stream and notify followers: preview the live schedule, then use `fanful_creator_live_schedule_create` or update with a matching `fanNotification` payload after exact confirmation. Fanful persists notification-center rows first, then attempts APNs/FCM push delivery for live-push-enabled recipients with active devices when server credentials are configured.
- Send Fanful events to another platform: create a workflow-trigger subscription, verify the signed webhook, dedupe by event/idempotency key, read the linked Fanful manifest, then call the external platform API.
