Log in Claim your YouTube handleClaim your handle
Developers

The fallo API

Read your fans and how your videos did, add fans from other tools, and get told the moment a fan joins or a video goes out. JSON in and out, one key per tool. No code? Use Zapier.

Quickstart: 2 minutes

From nothing to a working key, a new fan and a webhook.

  1. Make a key

    In fallo, open Integrations and press Make a key. Copy it straight away: it starts with fl_live_ and fallo shows it only once.

  2. Check it works

    curl https://fallo.ai/api/v1/me \
      -H "Authorization: Bearer fl_live_your_key"

    You get your channel back:

    {
        "channel": {
            "name": "Ana Cooks",
            "handle": "@anacooks",
            "fan_page": "https://fallo.ai/anacooks",
            "verified": true
        },
        "fans": 1284,
        "key": "My script"
    }
  3. Add a fan

    curl -X POST https://fallo.ai/api/v1/fans \
      -H "Authorization: Bearer fl_live_your_key" \
      -H "Content-Type: application/json" \
      -d '{"email": "sam@example.com", "source": "link"}'

    fallo emails Sam a confirm link. Sam is pending until they tap it, then active.

  4. Get told when fans join

    curl -X POST https://fallo.ai/api/v1/hooks \
      -H "Authorization: Bearer fl_live_your_key" \
      -H "Content-Type: application/json" \
      -d '{"url": "https://example.com/fallo-hook", "event": "fan.joined"}'

    Keep the secret in the answer: you’ll use it to check each call is from fallo. You can also add webhooks on Integrations.

Zapier (no code)

Don’t want to write code? The fallo app for Zapier connects fallo to thousands of apps: Google Sheets, Mailchimp, Discord, Slack and the rest.

Pick a fallo trigger, pick an app, and switch the Zap on. Zapier asks for an API key from Integrations.

Trigger: New FanTrigger: Fan LeftTrigger: Video SentAction: Add FanSearch: Find Fan
Use fallo in Zapier

More ideas on the Integrations page.

Authentication

Send your key on every request in the Authorization header (or as X-API-Key if your tool can’t set that header):

Authorization: Bearer fl_live_your_key
  • A key is fl_live_ plus 40 characters. fallo keeps only a fingerprint of it, so a lost key can’t be shown again: make a new one.
  • A key only sees its own channel.
  • Revoke a key on Integrations any time. It stops working at once, and webhooks made with it stop too.

Endpoints

Base URL: https://fallo.ai/api/v1. JSON in and out. Dates are UTC, in ISO 8601 (2026-10-04T15:02:11Z).

GET/meThe channel your key belongs to, your active fan count and the key’s name. Use it to check a key works.
GET/fansYour fans, newest first. Filters: status (active, pending, unsubscribed, bounced, removed), since (a date), limit (1–100, default 50). For the next page, pass cursor = the next_cursor you got (it’s null on the last page).
GET/fans/{id or email}One fan.
POST/fansAdd a fan: {"email": "…", "source": "link"}. They get the confirm email and join when they tap it. Answers 201 for someone new, 200 if they were already on your list. Your channel must be verified, and fallo’s spam limits apply.
DELETE/fans/{id or email}Stop emails to a fan (their status becomes removed, and fan.left fires).
GET/videosYour videos, newest first, with how many fans each was sent to, opened and watched. limit 1–100, default 20.
GET/statsTotals: active and pending fans, fans who joined in the last 7 days, emails sent and opened in the last 30 days, videos sent.
GET/hooksYour webhooks.
POST/hooksSubscribe a URL to an event (REST hooks, as Zapier uses): {"url": "https://…", "event": "fan.joined"}. The answer includes the webhook’s secret.
DELETE/hooks/{id}Unsubscribe a webhook.

The fan object

Lists come back as {"data": [ … ], "next_cursor": …}, one fan as {"data": { … }}. A fan looks like this:

{
    "id": "a1b2c3d4e5f6",
    "email": "sam@example.com",
    "status": "active",
    "source": "qr",
    "source_label": "QR code",
    "joined_at": "2026-10-04T15:02:11Z",
    "confirmed_at": "2026-10-04T15:03:40Z"
}
  • id: fallo’s id for the fan. Use it or their email in /fans/{id or email}.
  • status: pending (hasn’t confirmed yet), active, unsubscribed, bounced or removed.
  • source: where they came from, e.g. link, qr, desc, comment, post, bio, chat, social. When you add a fan, anything else becomes link. source_label is the same in words.
  • confirmed_at is null until they tap the confirm link.

POST /fans also tells you confirm_email_sent (true or false) and a note in plain words.

Errors

Errors come back with a matching HTTP status and a body like this:

{
    "error": {
        "code": "bad_email",
        "message": "email must be a valid email address."
    }
}
StatusCodeWhat it means
400bad_email bad_status bad_since bad_event bad_urlSomething in the request isn’t right. The message says what.
401unauthorizedNo key, or the key is wrong or revoked.
403not_verifiedVerify your channel on fallo before adding fans.
404not_foundNo such fan, or no such endpoint.
429rate_limitedOver 120 requests a minute. Wait for Retry-After seconds.
429not_sentfallo’s spam limits held back the confirm email. The message says why; try again later.

Rate limit

Up to 120 requests a minute per key. Over that, you get 429 with Retry-After: 60. Each key has its own limit.

Webhooks

Add a webhook on Integrations, or with POST /hooks, and fallo will POST to it when:

fan.joinedA fan confirmed and joined your list
fan.leftA fan stopped emails, bounced or was removed
video.sentA video finished sending to your fans

The body is JSON. data is a fan, or for video.sent a video:

{
    "event": "fan.joined",
    "created_at": "2026-10-04T15:03:40Z",
    "data": {
        "id": "a1b2c3d4e5f6",
        "email": "sam@example.com",
        "status": "active",
        "source": "qr",
        "source_label": "QR code",
        "joined_at": "2026-10-04T15:02:11Z",
        "confirmed_at": "2026-10-04T15:03:40Z"
    }
}
{
    "event": "video.sent",
    "created_at": "2026-10-04T16:05:12Z",
    "data": {
        "id": "dQw4w9WgXcQ",
        "title": "My studio tour",
        "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "kind": "upload",
        "status": "sent",
        "published_at": "2026-10-04T16:02:00Z",
        "sent_at": "2026-10-04T16:05:12Z",
        "sent_to": 1284,
        "opened": 0,
        "watched": 0
    }
}
  • Headers: X-Fallo-Signature (see below), X-Fallo-Event-Id (the same on every retry of one event) and User-Agent: Fallo-Webhooks/1.
  • Answer with any 2xx. If your side is down, fallo retries after 1, 5 and 30 minutes, 2 hours and 12 hours, then gives up on that event.
  • Answer 410 to unsubscribe. A webhook that fails 50 times in a row is switched off.
  • Webhook addresses must be public https:// URLs. Up to 25 per channel.
  • Send a test on Integrations posts a sample with "test": true.

Checking signatures

Every call carries X-Fallo-Signature: sha256=…, an HMAC-SHA256 of the raw body with the webhook’s secret (from POST /hooks). Check it before trusting the call, using the raw body, not re-encoded JSON:

// Node (with Express, use express.raw({ type: 'application/json' }) so rawBody is the exact bytes)
const want = Buffer.from('sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex'));
const got = Buffer.from(req.get('X-Fallo-Signature') || '');
if (got.length !== want.length || !crypto.timingSafeEqual(got, want)) return res.sendStatus(401);
// PHP
$rawBody = file_get_contents('php://input');
$sig = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
if (!hash_equals($sig, $_SERVER['HTTP_X_FALLO_SIGNATURE'] ?? '')) { http_response_code(401); exit; }

Good to know

  • Fans added through the API still confirm by email. fallo never emails anyone who didn’t say yes.
  • Keys can be revoked any time on Integrations; webhooks made with that key stop too.
  • Questions: hello@fallo.ai