ClickBank Instant Notification Service (INS): A Setup Guide for Affiliates

If you run ClickBank offers as an affiliate, you’ve probably assumed real-time sale tracking is a vendor-only thing. Something for the people who actually own the product, not the people promoting it. I assumed the same thing, right up until I needed to know which specific ad, headline, or landing-page variant a real commission actually came from. Guessing from ClickBank’s dashboard wasn’t going to cut it, so I sat down and built a proper integration myself.

This guide is everything I figured out doing that: what ClickBank’s own documentation says, plus the parts it conveniently leaves out, plus the specific things that broke on me while I was setting it up live against my own account. If you’re about to do this yourself, the second half should save you the hour or two I lost.


What Is INS, in Plain Terms?

Instant Notification Service (INS) is ClickBank’s real-time webhook. Normally, the only way to know a sale happened is to log into ClickBank and look at your reporting dashboard. You’re pulling the information yourself, on your own schedule, well after the fact. INS flips that around: the moment a transaction event happens on ClickBank’s end (a sale, a refund, a rebill, a chargeback), their servers immediately send an HTTP request to a URL you provide, with the details of exactly what happened.

For me, that’s the difference between “I think my new landing page did better this week” and actually knowing, in near real time, which specific page, ad, or tracking tag produced a specific dollar amount. It’s the piece that turns a ClickBank hoplink from a dumb redirect into something you can genuinely optimize against.

Wait, Can Affiliates Actually Use This?

Yes, and this is worth saying plainly because everything about how ClickBank presents the feature suggests otherwise. The entire setup screen lives inside a section of your account literally labeled “Vendor Settings.” There’s no separate “Affiliate Settings” version, no obvious affiliate-facing documentation, nothing that visually tells you this applies to you if you don’t sell your own product. I almost gave up on the idea entirely for exactly this reason, before I bothered double-checking.

It does apply to you. Every INS notification carries a role field (VENDOR, AFFILIATE, or JV_UPSELL) telling you which side of that specific transaction you were on. Since I’m a pure affiliate who only promotes other people’s products, the notifications for sales my hoplinks generated simply arrive with role: AFFILIATE, carrying a smaller, affiliate-appropriate slice of the data (details further down).

Requesting Access

INS isn’t switched on by default for any account. You have to ask ClickBank to enable it first, and approval isn’t instant. Here’s the path I followed:

  1. Log into ClickBank and select the account nickname you’re working in.
  2. Go to the Vendor Settings tab → My Site.
  3. Find the Advanced Tools section and click Edit.
  4. Next to the Instant Notification URL field, click Request Access.
  5. Fill out the form, actually read the terms of use, and acknowledge them.
  6. Click Save Changes & Request API Access, then Save Changes.

After that, it’s a waiting game. Give yourself some lead time before you can actually configure anything. I couldn’t touch the real fields until the request came back approved.

Setting It Up Once You’re Approved

Back in that same Advanced Tools panel, you’ll see a handful of fields:

Field What to put
Instant Notification URL The webhook endpoint on your own server. ClickBank gives you up to 10 separate slots, in case you want to notify multiple systems at once.
version Leave it at 8.0. That’s the current JSON-based notification format, and everything in this guide assumes it.
Secret Key Read the next section before you touch this field. It’s the part that actually cost me time.
Encrypt Transaction URLs / Encrypt TEST Transaction URLs Don’t confuse this with INS encryption. This is a separate feature that encrypts the confirmation/thank-you page redirect URL. INS notifications are encrypted regardless of this setting. I left both unchecked since neither applied to what I was doing.

The Secret Key: exactly 16 characters, uppercase letters and numbers only

This is the part ClickBank does not make obvious anywhere in the UI, and it burned me twice, in two different ways.

The hard rule, precisely: the Secret Key field accepts a maximum of 16 characters, and only uppercase A–Z and digits 0–9: no lowercase, no symbols, no spaces.

My first attempt broke the character rule. I generated a random key the way I normally would for something like this, it happened to include lowercase letters, and ClickBank’s form bounced it immediately with an on-page error: “Secret Key cannot be blank and must contain only uppercase letters and numbers.” Annoying, but at least it told me what was wrong. Easy fix: I just regenerated an uppercase-only version.

That’s when the second, much sneakier problem showed up. With the character rule satisfied, I generated a proper 32-character uppercase-alphanumeric key and saved it. No error this time. The field accepted it, looked fine, everything seemed set. Then real notifications started hitting my endpoint and every single one failed to decrypt, with absolutely no indication of why.

Figuring that out took some actual debugging (walked through in detail further down), but the short version: ClickBank’s field has no visible character counter and gives no warning when you exceed 16 characters. It just silently keeps the first 16 and throws away the rest. I didn’t find out until I went digging. Regenerating a key at exactly 16 characters and resaving it fixed the problem immediately.

If you want to skip my mistakes entirely, just generate it correctly the first time:

openssl rand -hex 8 | tr a-z A-Z

And make absolutely sure the value saved in ClickBank and the value in your own code are character-for-character identical.

The Payload Itself: Encrypted JSON, Not Plain Form Data

Here’s something else I didn’t expect going in: this isn’t a simple form-encoded POST with a few readable fields, the way a lot of webhook systems work. What actually arrives at your endpoint is a small JSON envelope wrapping an encrypted blob:

{"notification": "<base64-encoded ciphertext>", "iv": "<base64-encoded IV>"}

To get anything usable out of that:

  1. Base64-decode both notification and iv.
  2. Derive your AES key from the secret you set: substr(sha1(secretKey), 0, 32). That is, SHA-1 hash your secret key, then take the first 32 characters of the resulting hex string and use those characters literally as the key bytes. You do not hex-decode them first.
  3. Decrypt using AES-256-CBC with that derived key and the decoded IV.
  4. Trim off leftover padding bytes: trim($decrypted, "\0..\x20").
  5. json_decode what’s left. That’s finally your real notification data.

In code, the whole thing looks like this:

$envelope = json_decode(file_get_contents('php://input'), true);

$key = substr(sha1($secretKey), 0, 32);
$decrypted = openssl_decrypt(
    base64_decode($envelope['notification']),
    'AES-256-CBC',
    $key,
    OPENSSL_RAW_DATA,
    base64_decode($envelope['iv'])
);
$decrypted = trim($decrypted, "\0..\x20");
$notification = json_decode($decrypted, true);

There’s no separate signature header to verify, which honestly surprised me a little given how sensitive this data is. Authentication here is entirely implicit in the decryption itself: if your secret key is right, decryption succeeds and hands you valid JSON. If it’s wrong, PHP’s openssl_decrypt doesn’t return garbled text: it returns false outright, a very specific and recognizable “padding validation failed” signal that basically means “wrong key.” That one detail is what actually let me diagnose my own mess, which I’ll walk through now.

What Data You Actually Get as an Affiliate

Field What it is
transactionTime ISO 8601 timestamp of the transaction
receipt ClickBank’s receipt ID (8–21 characters). Note this can show up masked as ******** in sandbox/connectivity-check payloads, not just in real ones
transactionType What kind of event this is. See the full list below
vendor The vendor’s ClickBank nickname
affiliate Your affiliate nickname
role VENDOR, AFFILIATE, or JV_UPSELL for this specific transaction
totalAccountAmount The money that actually lands in your account: your commission. This is the number that matters to you as an affiliate.
paymentMethod e.g. VISA
version The payload format version, currently 8

It’s worth calling out totalAccountAmount specifically, because I almost made a wrong assumption here myself: ClickBank also has a totalOrderAmount field representing the full price the customer paid, but that’s the vendor’s number, not mine, and I don’t necessarily even receive it as an affiliate. If you’re building revenue reporting off this data, grab totalAccountAmount, not the order total.

The tracking parameters: affiliateTrackingParameters

Whatever tracking values you appended to your HopLink come back to you inside this nested object:

Field Notes
trafficType, trafficSource, offer, campaign, ad, adgroup, creative Free-text fields, 100–150 character limits
affSub1 through affSub5 This is your real attribution mechanism as an affiliate. There is no generic, catch-all “tid” tracking field the way you might expect from other networks. Instead you get five generic, named sub-ID slots. If you need to tag a click with your own identifier (which page variant it came from, which ad, whatever you’re testing), append something like &affSub1=yourvalue to your hoplink, and it comes straight back to you here when the sale closes.
uniqueAffSub1 through uniqueAffSub5 Same idea, with more room (256 characters each)
fbclid, extclid Click IDs, if you’re passing them through

Also nested: commonTrackingParameters

A smaller object containing clickId (a UUID ClickBank generates itself for the click) and trackingType (e.g. "hop").

What You Don’t Get

As an affiliate, you’re deliberately kept away from full customer personal information: no customer name, email, phone number, or full mailing address. Tax amount, shipping amount, currency, and download URLs are vendor-only too. You may still see the customer’s billing state, postal code, and country, but nothing more specific than that.

The Full List of Transaction Types

Type What it means Revenue impact
SALE Standard one-time or initial sale Add
BILL Rebill (a recurring subscription charge) Add
RFND Refund Subtract
CGBK Chargeback Subtract
INSF eCheck chargeback / insufficient funds Subtract
CANCEL-REBILL Recurring billing was canceled Ignore
UNCANCEL-REBILL A canceled subscription was reinstated Ignore
SUBSCRIPTION-CHG A subscription’s plan or price changed Ignore
ABANDONED_ORDER Checkout was started but never completed Ignore
TEST, TEST_SALE, TEST_BILL, TEST_RFND, CANCEL-TEST-REBILL, UNCANCEL-TEST-REBILL Sandbox versions of the above (sent to the vendor only, never the affiliate) Ignore
CUSTOMER_AUTH_FAILURE A recurring charge attempt failed authorization Ignore
CUSTOMER_EMAIL_UPDATE Customer updated the email on file Ignore
CUSTOMER_UPDATE_CC_NOTIFICATION Customer updated the card on file Ignore
PURCHASE_DETAILS_EMAIL_RESPONSE Customer replied to a purchase-details email Ignore

Only two buckets actually matter for revenue, as the table shows: SALE and BILL are money coming in, RFND, CGBK, and INSF are money going back out, logged as a negative amount against the same tracking value so my net revenue stays honest instead of only ever climbing. Everything else gets ignored.

What affiliates actually use INS for

Knowing how to build the pipe is one thing. Here’s what I actually use it for, and a few other ways to point the same mechanism at a different question.

Attributing sales back to a specific test. This is what got me building this in the first place. I run title A/B tests on my review pages, tagging each variant through affSub1, and INS is the only way to close the loop: which specific headline actually produced a paying customer, not just a click.

Feeding real conversion data into ad platforms. Google Ads and Meta only see what happens on your own domain. A hoplink click is not a sale. Once INS confirms a real SALE, that revenue can get pushed back into the ad platform as an offline conversion, so bidding optimizes toward buyers instead of cheap clicks.

Catching refund and chargeback patterns early. RFND and CGBK notifications arrive the same way a SALE does. Watching that stream by traffic source shows which campaign or which ad account is bringing in buyers who ask for their money back, instead of only noticing it a month later in a payout report.

Real-time alerts instead of a dashboard you have to remember to check. A Slack or Discord ping the moment a sale lands. During a fresh campaign launch this matters more than it sounds like it should. Knowing something converted an hour in changes what you do for the rest of the day.

Comparing traffic sources properly. affSub1 through affSub5 are yours to define, so nothing stops you from using more than one at once: one slot for the page or variant, one for the traffic source, one for the ad account. INS hands back a real revenue number broken out however you set it up, not just a click count.

None of this needs to be complicated. My own plugin only handles the first one so far. Everything else on this list uses the exact same affSub slots, just answering a different question.

What I Actually Learned Testing This Live

This is the part most guides skip, because most guides are written from the documentation instead of from actually wiring the thing up and watching it fail in real time. Here’s what really happened to me, in order.

First mistake: I tried a key with lowercase letters in it, and ClickBank flat-out rejected it. The form bounced it immediately with an on-page error: “Secret Key cannot be blank and must contain only uppercase letters and numbers.” Clear enough. I regenerated an uppercase-only key and moved on, thinking I was done.

Second mistake, and the one that actually cost me time: I used a 32-character key, and ClickBank accepted it without a single complaint. No error, no warning, field looked fully saved. I wired up my endpoint, waited for real notifications to arrive, and every single one failed to decrypt for no reason I could see.

Figuring that out took some real detective work. openssl_decrypt was returning false: not gibberish, a hard failure, which (per the encryption notes above) is the specific signature of “the key is wrong,” not “something else is broken.” Since my algorithm matched ClickBank’s documentation exactly, that pointed at a value mismatch rather than a formula mismatch on my end. Just to rule out a documentation error, I tested my actual captured ciphertext against half a dozen alternate key-derivation guesses: SHA-256 instead of SHA-1, MD5, the raw secret padded out, a lowercased version of the secret, and so on. None of them worked either, which was actually the useful result: it told me the formula was right and the secret itself was the mismatch. That’s when it clicked that ClickBank’s field must have quietly truncated my 32-character key down to something shorter. I regenerated a proper 16-character key, resaved it, and the very next notification decrypted perfectly.

Third mistake, more of a false alarm: I clicked “Send Test INS” expecting a test notification, and instead got ClickBank’s own generic error page. (“The page you requested has generated an error…”) Turns out that’s expected, not a sign I’d broken anything. ClickBank’s documentation states plainly that test transactions only ever go to the vendor role, never the affiliate. Since my account has no vendor product of its own, there was nothing for ClickBank’s backend to generate a test sale against, so it just errored out instead.

The genuinely useful surprise: saving/verifying my notification URL DID cause ClickBank to fire something at it, just not a real sale. Right around when my settings showed as “verified,” a real request hit my endpoint from ClickBank’s own infrastructure and successfully decrypted with my (by then correct) secret key. That was a great sign: it confirmed my entire encryption implementation was correct against ClickBank’s actual live system, not just against payloads I’d faked myself for testing. But the notification itself was role: VENDOR, transactionType: TEST, using obviously canned sandbox data: a placeholder vendor nickname, a “Test User” customer, a product literally titled “A passed in title,” and a masked receipt. It’s ClickBank’s own generic connectivity check, unrelated to any real transaction of mine, genuinely useful for proving my endpoint was reachable and my decryption worked, but not something to mistake for a real test. My code correctly ignored it rather than logging it as an actual sale.

The real, final test was still an actual sale. There’s no way around that as an affiliate. I just had to wait for one. Everything above meant that by the time it happened, I already knew my endpoint, my secret key, and my decryption code all worked correctly. The only thing left to confirm was whether my own affSub tracking values would come through the way I expected, and on my first real sale, they did.

INS vs. S2S (Server-to-Server) Tracking

“S2S” is the general term the affiliate and ad-tech world uses for this same basic pattern: trackers like Voluum, RedTrack, and Binom, and most ad networks, all support some version of it. The network calls a URL on your own server the instant a conversion happens, instead of relying on a client-side pixel that ad blockers and browser privacy settings can and do kill. INS is ClickBank’s specific implementation of that general idea, but it does a few things differently from what I’d expect coming from a typical tracker:

Typical generic S2S postback ClickBank INS
Payload format Plain query-string GET, or a simple form/JSON POST Encrypted JSON (AES-256-CBC)
Authentication A shared token or secret, usually passed as a plain query parameter or header No header signature at all: authenticity is implicit in whether decryption succeeds
Tracking ID passthrough Usually one arbitrary click_id or sub_id macro you name yourself A fixed set of named slots (affSub1–affSub5, plus semantic ones like campaign, creative)
Data visibility Typically the same payload no matter who’s receiving it Role-aware: the vendor and the affiliate literally see different fields for the same transaction
Testability Most networks let you fire a test postback on demand Test transactions are vendor-only; there’s no way for an affiliate to trigger a real test

Short version: INS is ClickBank’s flavor of S2S tracking. It’s just built with an unusual mandatory encryption layer and a role-aware data model that most generic postback systems don’t bother with.

Quick Reference Checklist

  • Request INS access (Vendor Settings → My Site → Advanced Tools) and wait for approval.
  • Generate a secret key that is exactly 16 characters, uppercase letters and numbers only: nothing longer, nothing lowercase, nothing with symbols. (I learned this one the hard way, twice.)
  • Set that same secret key in both ClickBank and your own receiving code, character for character.
  • Set your Instant Notification URL, and leave version at 8.0.
  • Append affSub1 (or another available slot) to your hoplinks with whatever value you’ll need to attribute a sale back to later.
  • On the receiving end: decode the {notification, iv} envelope, decrypt with AES-256-CBC using a key derived as substr(sha1(secret), 0, 32), trim the result, then JSON-decode it.
  • Treat SALE/BILL as positive revenue and RFND/CGBK/INSF as negative, and ignore every other transaction type.
  • Don’t expect to test this with a button. Expect a generic vendor-role sandbox ping when you first save your settings, and treat your first real sale as the actual test. That’s what it was for me.
Author Profile

My name is Jay and I have been doing affiliate marketing since around 2014. I started with CPA offers and "rent-to-own" offers on MaxBounty and soon switched to Amazon Associates driving SEO traffic from niche websites. I now mostly do paid traffic and run affiliate stuff with email marketing.