API v4.4 Publisher DashboardDashboard
📡 Publisher Integration Guide

FastCoins Publisher API
Documentation

Everything you need to integrate PTC ads on your site — Postback S2S notifications and full API reference.

Postback S2S PTC API Bearer Token Auth PHP Examples Free & Public
Overview

FastCoins is a PTC (Paid-To-Click) publisher network. Embed our ads on your site, your users earn token rewards for viewing them, and you receive a revenue share automatically.

📡
Postback (S2S)
Your server gets notified via HTTP GET when a user completes an ad. Ideal for automatic reward crediting without client-side code.
🔌
PTC API
Fetch available ads programmatically and display them with your own custom UI. No widget embedding required.
🪙
Custom Tokens
Configure your own token name and conversion rate. Rewards are automatically converted and sent via postback.
💰
Revenue Split
You control the user payout percentage. The rest is your publisher revenue — withdrawn monthly (days 1–3).
🚀
Getting Started

Create a publisher account, verify you own your domain, and get your API credentials once it's reviewed.

  • 1
    Create a publisher account at FastCoins.com/publisher_dashboard. You'll need a site name and URL.
  • 2
    Add a domain in the Domains tab. Each domain has its own API Key, Secret Key, Bearer Token, token config, and postback URL.
  • 3
    Set your postback URL — the endpoint on your server that receives reward notifications (see Postback section below).
  • 4
    Generate a Bearer Token from the Domains tab. Required to authenticate PTC API calls.
  • 5
    Send a test postback from the Domains tab to verify your endpoint responds correctly before going live.
ℹ️
Withdrawals: Publisher balances can only be withdrawn from the 1st to the 3rd of each month. Plan your cash-flow accordingly.
📬
Postback Integration (S2S)

A server-to-server notification sent to your website when a user completes an ad view, so you can automatically credit rewards without any client-side code.

🔐 Security Requirements

⚠️
Always validate the source IP and the signature before crediting any reward. Never credit based on parameters alone.
  • Authorized server IPs: 62.171.140.250, 2a02:c207:2329:8601::1 — check this against $_SERVER['REMOTE_ADDR'] only. This is a direct server-to-server call, not a browser request, so there's no proxy/CDN of ours in between; a header like X-Forwarded-For is client-supplied and trivial to spoof, so don't trust it for this check.
  • userIp is required, not just informational — reject the callback if it's missing or not a valid IP. We always resolve and send the real IP of the viewer who completed the ad (through Cloudflare on our side) specifically so you can rely on it for fraud detection, IP-based limits, and duplicate-user checks — an endpoint that ignores it loses that signal entirely.
  • Required HTTP response: exactly ok — lowercase, no spaces, no HTML, HTTP 200
  • Signature algorithm (recommended): signature_sha256 = HMAC-SHA256(subId + transId + reward, secret_key)secret_key is the HMAC key, not concatenated into the message. userIp is not part of it.
  • Signature algorithm (legacy, still sent on every callback): signature = MD5(subId + transId + reward + secret_key) — values concatenated with no separators. Kept working indefinitely for integrations built before signature_sha256 existed; nothing to change if you already verify this one.
  • Need more than 8 decimals? Every callback also carries reward_precise (10 decimals) with its own signature_sha256_precise = HMAC-SHA256(subId + transId + reward_precise, secret_key) — fully optional and additive, computed the same way, just over the higher-precision string. reward/signature_sha256/signature keep sending exactly as before regardless of whether you use it.
  • Duplicate prevention: always check transId in your DB before crediting

📋 Postback Parameters (GET)

Your postback URL receives these parameters via HTTP GET.

Parameter Type Required Description
subIdstringREQUIREDUnique user ID in your system — the value you passed in the widget/API call
transIdstringREQUIREDUnique transaction ID — always check this to prevent duplicate credits
rewardfloatREQUIREDReward in your token currency (already converted using your configured token_rate), formatted to 8 decimals
reward_precisefloatOptionalSame reward as reward, formatted to 10 decimals instead of 8 — use this when 8 decimals isn't enough precision for you
reward_namestringREQUIREDToken name as configured for your domain (e.g. POINTS, COINS)
reward_valuefloatREQUIREDSame as reward — included for bitcotasks compatibility
offer_namestringOptionalTitle of the completed ad — useful for logging
offer_typestringOptionalAlways ptc for FastCoins ad views
payoutfloatOptionalSame as reward — included for third-party compatibility
userIpstringREQUIREDReal IP address of the user who completed the offer, resolved through Cloudflare on our side before we send this callback — reject the callback if this is missing or fails FILTER_VALIDATE_IP. Use it for fraud detection, IP-based limits, duplicate-user detection, conversion auditing, and analytics.
statusintegerREQUIRED1 = Credit reward  |  2 = Chargeback (deduct reward)
signature_sha256stringREQUIREDHMAC-SHA256 of subId + transId + reward, keyed with secret_key — recommended for new integrations
signature_sha256_precisestringOptionalHMAC-SHA256 of subId + transId + reward_precise, keyed with secret_key — verify this instead of signature_sha256 only if you're also reading reward_precise
signaturestringREQUIREDLegacy: MD5 of subId + transId + reward + secret_key concatenated (no separators). Still sent on every callback, unchanged — kept for integrations built before signature_sha256 existed.
testintegerOptional1 = test postback from dashboard — do not credit real rewards

🧮 Example: Building & Verifying a Signed URL

A full worked example so you can sanity-check your own signature calculation against ours before going live. This uses a made-up secret key purely for illustration.

example
Secret Key (example only, never a real one): demo_secret_do_not_use_9f8e7d6c

Parameters for this conversion:
  subId   = user_12345
  transId = txn_987654321
  reward  = 0.50
  status  = 1
  userIp  = 127.0.0.1

signature_sha256 = HMAC-SHA256(subId + transId + reward, secret_key)
                  = HMAC-SHA256("user_12345" . "txn_987654321" . "0.50", demo_secret_do_not_use_9f8e7d6c)
                  = HMAC-SHA256("user_12345txn_9876543210.50", demo_secret_do_not_use_9f8e7d6c)
                  = df3eafaa3e8cf4fe8fb40c7bed633f34bc7b964bca58d2d691314771a042d59c

signature (legacy) = MD5(subId + transId + reward + secret_key)
                    = MD5("user_12345" . "txn_987654321" . "0.50" . "demo_secret_do_not_use_9f8e7d6c")
                    = MD5("user_12345txn_9876543210.50demo_secret_do_not_use_9f8e7d6c")
                    = d1eb68e41cbd5770de9384c5be8ebb39

Resulting callback URL:
https://yoursite.com/postback.php?subId=user_12345&transId=txn_987654321&reward=0.50&status=1&userIp=127.0.0.1&signature=d1eb68e41cbd5770de9384c5be8ebb39&signature_sha256=df3eafaa3e8cf4fe8fb40c7bed633f34bc7b964bca58d2d691314771a042d59c
⚠️
Never use this example secret for anything real — it's public, printed in this documentation. Compute your own signatures with the actual Secret Key from your Domains tab, and never paste that real key anywhere public (chat tools, docs, public repos) — treat it exactly like a password. This example also omits reward_name, reward_value, offer_name, offer_type, payout and test for brevity — your real callback includes all of them, per the table above.

📐 Optional: 10-Decimal Precision (reward_precise)

Same secret, same callback — just a reward with more fractional precision than reward's 8 decimals can hold. Fully additive: skip this if 8 decimals already covers your use case.

example-precise
Same secret key and subId/transId as above, but the underlying reward this
time carries more precision:

  reward          = 0.12345679   (8 decimals, rounded)
  reward_precise  = 0.1234567890 (10 decimals)

signature_sha256_precise = HMAC-SHA256(subId + transId + reward_precise, secret_key)
                          = HMAC-SHA256("user_12345" . "txn_987654321" . "0.1234567890", demo_secret_do_not_use_9f8e7d6c)
                          = 219d2101a528b8585161bd26c3bb80c5de8a6741d60d2f1778eb805a05f7a707

💻 PHP Implementation — postback.php

Save this on your server, set your Secret Key from the Domains tab, then paste the full URL into the Postback URL field of your domain.

postback.php
// ── 1. YOUR SECRET KEY (from Domains tab → Secret Key) ─────
$secret = "";  // ← paste your secret key here

// ── 2. IP VALIDATION (callback source) ──────────────────────
// REMOTE_ADDR only — this call comes straight from our server to
// yours, no browser and no proxy of ours in between, so it's the
// one value here that can't be spoofed by whoever is calling you.
// X-Forwarded-For (or any other header) is attacker-controlled and
// must never be trusted for this check.
$allowed_ips = ['62.171.140.250', '2a02:c207:2329:8601::1'];
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
if (!in_array($ip, $allowed_ips, true)) {
    echo "ERROR: Invalid source IP";
    exit;
}

// ── 3. COLLECT PARAMETERS ───────────────────────────────────
$userId    = $_GET['subId']     ?? null;
$transId   = $_GET['transId']   ?? null;
$status    = $_GET['status']    ?? null;
$isTest    = isset($_GET['test']) && $_GET['test'] == 1;
$userIp    = $_GET['userIp']   ?? null;  // real IP of the ad viewer — required, see step 4

// ── 3b. DYNAMIC PRECISION ────────────────────────────────────
// Prefer reward_precise (10 decimals) when the callback carries it;
// fall back to the standard reward (8 decimals) otherwise. Whichever
// pair is picked, it's verified against its own matching signature in
// step 6 — if that signature is valid, the postback is accepted.
$hasPrecise = isset($_GET['reward_precise'], $_GET['signature_sha256_precise']);
$reward    = $hasPrecise ? $_GET['reward_precise']           : ($_GET['reward'] ?? null);
$signature = $hasPrecise ? $_GET['signature_sha256_precise'] : ($_GET['signature_sha256'] ?? null);

// ── 4. VALIDATE USER IP (required) ──────────────────────────
// userIp is mandatory — this is the real IP of the person who
// completed the offer, already resolved through Cloudflare on our
// end. Reject the callback outright rather than silently accepting
// one without it, since your fraud detection / IP-limit / duplicate-
// user checks depend on this value actually being present and valid.
if (!$userIp || !filter_var($userIp, FILTER_VALIDATE_IP)) {
    echo "ERROR: Invalid user IP";
    exit;
}

// ── 5. VALIDATE REQUIRED PARAMS ─────────────────────────────
if (!$userId || !$transId || $reward === null || !$signature) {
    echo "ERROR: Missing parameters";
    exit;
}

// ── 6. VERIFY SIGNATURE ─────────────────────────────────────
// Only subId + transId + reward go into this hash — userIp is NOT
// part of it. $secret is the HMAC key, not concatenated into the
// message. Don't add userIp here unless a different provider's docs
// explicitly say their signature scheme includes it.
$expected = hash_hmac('sha256', $userId . $transId . $reward, $secret);
if (!hash_equals($expected, $signature)) {
    echo "ERROR: Signature mismatch";
    exit;
}

// ── 7. HANDLE CHARGEBACK (status = 2) ───────────────────────
if ((int)$status === 2) {
    $reward = -abs((float)$reward);  // negative = deduct
}

// ── 8. TEST CALLBACK ─────────────────────────────────────────
// Authenticated and validated like any other callback, but must not
// touch a real balance.
if ($isTest) {
    echo "ok";
    exit;
}

// ── 9. PREVENT DUPLICATES, CREDIT REWARD & STORE USER IP ──────
// Replace with your actual DB logic.
if (!isTransactionProcessed($transId)) {
    creditUserReward($userId, $reward, $transId);
    // If your credit function only takes these 3 args, store $userIp
    // with the conversion separately, e.g.:
    //   saveConversionIp($transId, $userId, $userIp);
}

// ── 10. REQUIRED RESPONSE ───────────────────────────────────
echo "ok";  // MUST return exactly this, nothing else

🕓 Legacy Verification — MD5

💡
Already verifying signature with MD5? It's still sent on every callback, completely unchanged — this is here purely for reference. No action needed. New integrations should use signature_sha256 above instead.
postback-legacy.php
$signature = $_GET['signature'] ?? null;
$expected  = md5($userId . $transId . $reward . $secret);
if (!hash_equals($expected, $signature)) {
    echo "ERROR: Signature mismatch";
    exit;
}
// ...same IP check, userIp check, and crediting logic as the main example above.

🔀 Callback Flow

  • 1
    Callback received
  • 2
    Check REMOTE_ADDR against the IP whitelist
  • 3
    Read parameters, including userIp
  • 4
    Validate userIp — reject if missing or not a valid IP
  • 5
    Validate the other required parameters
  • 6
    Verify the signature
  • 7
    Check status — apply as chargeback if 2
  • 8
    If test=1, return ok without crediting anything
  • 9
    Check transId against your DB to prevent duplicate credits
  • 10
    Credit or reverse the reward, and store userIp with the conversion
  • 11
    Return ok

Possible responses:

  • ok — callback processed (or validated as a test callback)
  • ERROR: Invalid source IP
  • ERROR: Invalid user IP
  • ERROR: Missing parameters
  • ERROR: Signature mismatch
Required response: Return HTTP 200 with body exactly ok — no spaces, no newlines, no HTML.
⚠️
Failed postbacks can be resent from the Postback Logs tab in your dashboard. Always make your endpoint idempotent — safe to call multiple times for the same transId.
💡
Test first: Use the Test Postback button in the Domains tab to verify your endpoint before going live. Test postbacks include test=1 — skip crediting for those.
🔌
PTC API — Fetch Ads Programmatically

Query available PTC ads via a REST API and render them with your own custom UI. No widget embedding required.

🌐 Endpoint

GET https://FastCoins.com/api/[API_KEY]/[USER_ID]/[USER_IP]/[DEVICE]
SegmentDescription
API_KEYYour domain's API Key — found in the Domains tab of your Publisher Dashboard
USER_IDUnique identifier of the user in your system. Returned as subId in the postback
USER_IPReal IP address of the user — used for ad targeting and fraud prevention. Pass the actual client IP
DEVICEExactly desktop or mobile — there is no third value. Tablets (iPad, Android tablets) count as mobile. The visitor's real device — required once any advertiser is running a device-targeted campaign: since you're calling this API from your own server, your server's User-Agent isn't the visitor's, so we can't detect it for you — you must detect it and pass it through. Sending anything other than desktop/mobile (including a literal "tablet") is treated the same as omitting it — map it to mobile yourself. A request missing or misspelling this returns HTTP 400 in that case.
⚠️
Breaking change: if your integration doesn't send [DEVICE] yet, add it now. It's silently ignored today, but starts returning HTTP 400 as soon as device targeting goes live — don't wait for that to break your feed.

🔑 Authentication — Bearer Token

⚠️
Every API request requires a Bearer Token in the Authorization header. Generate one per domain from the Domains tab (🔄 button next to "Bearer Token").
Authorization: Bearer [YOUR_BEARER_TOKEN]
ℹ️
If a domain has no Bearer Token configured, authentication is skipped. Always set one for production environments.

💻 PHP Example

get-ads.php
// ── YOUR CREDENTIALS (from Domains tab) ─────────────────────
$apiKey      = 'YOUR_API_KEY';
$bearerToken = 'YOUR_BEARER_TOKEN';

// ── USER DATA ────────────────────────────────────────────────
$userId = $_SESSION['user_id'];  // your user's unique ID
// REMOTE_ADDR by default — it's the one value here that can't be
// spoofed by the visitor. Only read a proxy header instead (e.g.
// CF-Connecting-IP) if your own server sits behind a reverse proxy
// you control and trust — never a client-supplied header like
// X-Forwarded-For, which the visitor's own browser can set to
// anything.
$userIp = $_SERVER['REMOTE_ADDR'] ?? '';

// Required once any advertiser runs a device-targeted campaign — this
// script call happens during the visitor's own page load, so THIS
// server's incoming User-Agent is theirs. Don't skip this: a call from a
// background job/cron with no real visitor attached has no correct value
// to send here.
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$device    = preg_match('/Mobi|Android|iPhone|iPod|iPad|BlackBerry|IEMobile|Opera Mini/i', $userAgent)
    ? 'mobile' : 'desktop';

// ── BUILD URL ────────────────────────────────────────────────
$url = 'https://FastCoins.com/api/' . $apiKey
     . '/' . urlencode($userId)
     . '/' . urlencode($userIp)
     . '/' . $device;

// ── CURL REQUEST ─────────────────────────────────────────────
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 10,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $bearerToken,
        'Accept: application/json',
    ],
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($response, true);

// ── HANDLE RESPONSE ──────────────────────────────────────────
if ($httpCode === 200 && $data['status'] === '200') {
    foreach ($data['data'] as $ad) {
        // $ad['url'] already contains the user subId encoded
        echo '<a href="' . htmlspecialchars($ad['url']) . '" target="_blank">';
        echo htmlspecialchars($ad['title']);
        echo ' — ' . $ad['reward'] . ' ' . $ad['currency_name'];
        echo '</a><br>';
    }
} else {
    echo 'No ads available.';
}

📦 Response Format

Successful responses return HTTP 200 with this JSON structure:

{ "status": "200", "message": "success", "data": [ { "id": "123", "image": "https://FastCoins.com/banners/ad.png", "title": "Visit Our Site & Earn", "description": "Watch this ad for 30 seconds", "duration": "30", "reward": "100.0000", "currency_name": "Coins", "url": "https://FastCoins.com/view/abc123", "boosted_campaign": false, "ad_type": "Iframe" } ] }
FieldTypeDescription
idstringUnique identifier of the ad
imagestringFull URL of the ad banner image — may be empty
titlestringDisplay title — show this to your users
descriptionstringShort description of the ad
durationstringRequired view time in seconds: 2, 30, or 60
rewardfloatReward amount in your domain token currency (already converted)
currency_namestringToken name as configured for your domain (e.g. COINS, USDT)
urlstringRedirect the user here to watch the ad. Postback fires automatically on completion. Already encodes the user's subId.
boosted_campaignbooleanWhether this is a promoted ad — consider highlighting these in your UI
ad_typestringIframe = plays inside a frame  |  Redirect = user goes to advertiser's page

⚠️ Error Codes

Error responses share the same JSON structure with a non-200 status field.

HTTPstatusCause & Resolution
401401Invalid API key — check the API_KEY segment in the URL, or Bearer Token is missing when domain requires one
403403Bearer token mismatch — regenerate and update it in the Domains tab
400400Invalid USER_ID format — must be alphanumeric (a-z, 0-9, _, -, .), max 255 chars
400400Missing or invalid DEVICE segment — only returned once device targeting is live. Must be exactly desktop or mobile
500500Internal server error — the message field contains debug details. Contact support if this persists.
🔄
Integration Flow

End-to-end flow from API call to reward credit on your site.

📡 API + Postback Flow

Your site calls
GET /api/…
User clicks ad url
User watches ad on FastCoins.com
FastCoins fires
GET postback.php
Validate & credit reward
Return ok

🧩 Widget Embed (alternative)

If you prefer not to use the API, embed the widget as an iframe — it handles everything automatically, style-isolated from the rest of your page.

widget-embed.php
<iframe src="https://fastcoins.click/widget-embed.php?api_key=YOUR_API_KEY&sub_id={USER_ID}"
    style="width:100%;height:640px;border:none;" loading="lazy"></iframe>
ℹ️
Replace YOUR_API_KEY with your domain's API Key and {USER_ID} with the current user's ID (server-side rendered). The widget displays available ads and tasks inline and fires the postback automatically on completion. Adjust the 640px in style to make the iframe taller or shorter for your layout.
⚠️
One unique sub_id (= {USER_ID}) per real user — no suffix splitting. Each of your users must be identified by a single, stable id. You choose the format — an opaque id, a username, an email (e.g. ABD01230A23, [email protected]) — but one real user maps to exactly one id, forever.

Not permitted: taking one base id and appending a numeric suffix to manufacture many "users" out of one real person. The detection is separator-agnostic — it strips a trailing -, _ or . followed by digits and treats the rest as the user's identity. Any of these pairs is rejected:
  • 123-1 & 123-2
  • ABC_1 & ABC_2
  • A23-23 & A23-24
  • order.1 & order.2
A continuous id with no separator is one unique identity and is never split (ABD01230A23, 1001, [email protected] are all fine).

When the pattern is detected, the API still answers ok but returns a message explaining the id is not allowed and no reward is credited; the attempt is logged as fraud.
Tasks (CPA) — for advertisers

Create a task (e.g. "register at my site"), fund N completions, and confirm each one with a signed server-to-server postback. The user is credited automatically the moment your postback arrives — no manual approval, no platform hold.

📡 How it works

You create the task
/create-task
User opens your URL with cka_cid
User completes the action
Your server calls our postback
User credited instantly

1) The postback URL

GET https://fastcoins.click/webhooks/task-postback.php?cka_cid={CLICK_ID}&sign={SIGN}
  • cka_cid — the click id we appended to your destination URL when the user started (your-url?cka_cid=…). Echo it back unchanged.
  • signhash_hmac('sha256', $cka_cid, $task_secret) (lowercase hex). The per-task secret is shown in Create Task → My Tasks.

2) Signature

// PHP
$sign = hash_hmac('sha256', $cka_cid, $task_secret);

// Node.js
const sign = crypto.createHmac('sha256', taskSecret).update(ckaCid).digest('hex');

# Python
import hmac, hashlib
sign = hmac.new(task_secret.encode(), cka_cid.encode(), hashlib.sha256).hexdigest()
ℹ️
Only your whitelisted server IPs may call the postback (configured per task). Any other IP gets 403 even with a valid signature. This + the HMAC is the whole authenticity model — keep your secret safe.
⚠️
Credit is immediate and final on your postback — there is no chargeback. A completion is credited the instant your signed postback verifies, because your postback IS the proof the user did the work. Make sure your own fraud checks run before you call the postback: once sent, it can't be reversed. Only call it for completions you're confident are genuine.

3) Optional analytics pixel

Place this on your conversion page for your own analytics. It never credits — the postback above is the only thing that does.

<img src="https://fastcoins.click/task-pixel.php?cid={CLICK_ID}" width="1" height="1" alt="">
💡
Best Practices

Recommendations for a reliable, secure, and high-revenue integration.

⚡ Performance

  • Cache API responses for at least 60 seconds per user. Don't call the API on every page load — ads change at most every few minutes.
  • Pass the real user IP. Use REMOTE_ADDR by default — it's the one value that can't be spoofed by the visitor. Only read a proxy header instead (e.g. CF-Connecting-IP) if your own server sits behind a reverse proxy you control and trust; never a client-supplied header like X-Forwarded-For. Incorrect IPs reduce ad targeting quality.
  • Set a 10-second timeout on your cURL calls to prevent slow page loads if our API is temporarily slow.

🔒 Security

  • Never expose your Secret Key in client-side code or public repositories. It is used only server-side for signature validation.
  • Always check transId against your database before crediting — this prevents duplicate rewards from accidental postback resends.
  • Validate source IP before processing any postback. Only accept requests from 62.171.140.250 or 2a02:c207:2329:8601::1.
  • Use HTTPS for your postback endpoint. HTTP endpoints may be blocked.
  • Send one unique sub_id per real user. Don't append numeric suffixes to one base id (123-1/123-2, ABC_1/ABC_2, order.1/order.2) — any separator (- _ .) is detected as fraud and rejected with no reward credited. See the rule above.

💰 Revenue

  • Boosted ads (boosted_campaign: true) pay more — highlight them in your UI to increase click-through rates.
  • Longer ads (60s) pay significantly more than short ads (2s). Prioritize them when user engagement is high.
  • Balance your user share — a higher payout attracts more active users; a lower payout increases your own margin.
  • Withdrawals are open from the 1st to 3rd of each month — mark it in your calendar.

🐛 Debugging

  • Use the Test Postback button in the Domains tab to verify your endpoint returns exactly ok with HTTP 200.
  • Check the Postback Logs tab for full HTTP responses on every postback attempt — it shows status codes and server replies.
  • Failed postbacks can be resent individually or in bulk from the Logs tab. No data is ever permanently lost.
  • If the signature always fails: for signature_sha256, verify secret_key is passed as the HMAC key (not concatenated into the message) and you're hashing subId + transId + reward with no separators; for the legacy signature, verify you're concatenating subId + transId + reward + secret with no separators. Either way, make sure reward matches the exact string from the GET parameter (not rounded).
  • If you're verifying signature_sha256_precise: it's hashed over reward_precise, not reward — mixing the two fields with the wrong signature is the most common mistake here.

Ready to start earning?

Create your publisher account in under 2 minutes, then verify your domain to get started.

Open Publisher Dashboard →
✓ Copied to clipboard