How deals work
- Step 1
Buyer purchases
On ANKY.AI, in INR or USD. ANKY.AI handles payment, GST invoice and refunds.
- Step 2
Code is issued
One code per unit bought, e.g. ANKY-7KQ2M-X4PZC-9WJ3T-HB8RD. Stacked codes = several units.
- Step 3
Buyer redeems on your site
They sign in or sign up on your product and enter the code on your redeem page.
- Step 4
You activate the plan
Your server redeems the code through our API and turns on the plan it returns — for life, or for the deal's term.
Codes are cryptographically signed, so they can't be guessed or forged, and they tell you the deal and term they belong to. If a buyer is refunded, ANKY.AI revokes the code and sends you a signed code.revoked webhook so you can remove the access.
What you need to build
- A redeem page at the URL you give us (e.g.
https://yourapp.com/redeem/anky). Accept an optional?code=query parameter to prefill the field — our “Redeem” buttons link there with the code. - Require an account. Ask the buyer to sign in or sign up before redeeming, so the plan attaches to the right user. Pass your stable user id as
accountId. - Verify, then redeem, from your server (never from the browser — your partner key is a secret). Call
verifyto show what the code unlocks, thenredeemwhen the user confirms. - Activate the returned plan. Use
planandaccessEndsAtfrom the redeem response. Lifetime deals returnnull— the plan never expires. Fixed-term deals return an end date: downgrade the account then (don't auto-charge; offer your normal renewal). - Support stacking if your deal allows more than one code: each additional code redeemed on the same account returns the higher tier in
planand the runningstack.count. - Store every redeemed code with a UNIQUE constraint, so a code can never activate two accounts even if two requests race.
- Handle revocations. Expose a webhook endpoint, verify the signature, and on
code.revokedremove the access that code granted (and lower the stack tier if the account had several). - Show the plan source in billing settings (“Lifetime plan via ANKY.AI”) and hide upgrade prompts that would charge deal users for what they already bought.
Deal terms
| Term | Access after redemption | Default redeem window | Buyer refund window |
|---|---|---|---|
Monthly MONTHLY | 1 month | 30 days after purchase | 7 days |
Quarterly QUARTERLY | 3 months | 60 days after purchase | 14 days |
Semi-Annual SEMI_ANNUAL | 6 months | 90 days after purchase | 30 days |
Annual ANNUAL | 12 months | 90 days after purchase | 30 days |
Lifetime LIFETIME | Forever (life of the product) | 365 days after purchase | 60 days |
Fixed-term access starts when the code is redeemed, not when it was bought. Deals are prepaid — neither ANKY.AI nor you charge the buyer again.
Partner API
Base URL https://anky.ai/api/partner/v1. Authenticate every request with Authorization: Bearer <partner key>. You only ever see codes for your own deals — codes of other partners look invalid.
POST /codes/verify
// Node 18+ — check a code before showing your "activate" screen
const res = await fetch("https://anky.ai/api/partner/v1/codes/verify", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ANKY_PARTNER_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ code: input }), // any case, dashes/spaces optional
});
const result = await res.json();
// { valid: true, status: "issued", deal: { term: "LIFETIME", plan: "Starter",
// stackLimit: 3, stackTiers: [...] }, redeemBy: "2027-09-24T00:00:00Z", ... }POST /codes/redeem
Idempotent per account: redeeming the same code for the same account again returns 200 with alreadyRedeemed: true.
// After the user is signed in on YOUR site
const res = await fetch("https://anky.ai/api/partner/v1/codes/redeem", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ANKY_PARTNER_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ code, accountId: user.id, email: user.email }),
});
if (!res.ok) {
const { error } = await res.json(); // e.g. { code: "already_redeemed", message: "…" }
return showError(error.message);
}
const r = await res.json();
// { redeemed: true, plan: "Pro", perks: [...], stack: { count: 2, limit: 3 },
// term: "ANNUAL", accessStartsAt: "…", accessEndsAt: "2027-09-24T…" | null }
await db.transaction(async (tx) => {
await tx.insert(redeemedCodes).values({ code: r.code, userId: user.id }); // UNIQUE(code)
await tx.update(users).set({
plan: r.plan,
planSource: "anky",
planEndsAt: r.accessEndsAt, // null = lifetime
}).where(eq(users.id, user.id));
});GET /deals
Your deals with term, plan, stack tiers, redeem window and redeem URL.
Errors
| 401 | unauthorized | Missing or wrong partner key. |
| 404 | code_invalid | Not a genuine code, or not one of your deals. |
| 409 | already_redeemed | Code was redeemed on a different account. |
| 409 | stack_limit | Account already holds the maximum codes for this deal. |
| 410 | code_revoked | Refunded or revoked — don't activate. |
| 410 | code_expired | Not redeemed within the deal's redeem window. |
Webhooks
Give us an HTTPS endpoint and we'll share a signing secret. Each delivery has an X-Anky-Signature: t=<unix>,v1=<hex> header — an HMAC-SHA256 of `${t}.${rawBody}`, the same scheme as Stripe. Events: code.revoked (refund, chargeback or abuse). Respond with any 2xx.
import { createHmac, timingSafeEqual } from "node:crypto";
// POST /webhooks/anky — raw body required for the signature
export async function POST(req) {
const raw = await req.text();
const header = req.headers.get("x-anky-signature") ?? ""; // "t=1790000000,v1=<hex>"
const t = Number(/t=(\d+)/.exec(header)?.[1]);
const v1 = /v1=([a-f0-9]+)/.exec(header)?.[1] ?? "";
const expected = createHmac("sha256", process.env.ANKY_WEBHOOK_SECRET)
.update(`${t}.${raw}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - t) < 300;
if (!fresh || v1.length !== expected.length ||
!timingSafeEqual(Buffer.from(v1), Buffer.from(expected))) {
return new Response("bad signature", { status: 400 });
}
const event = JSON.parse(raw);
if (event.type === "code.revoked") {
// Refund / chargeback: remove the access this code granted.
await deactivateCode(event.data.code, event.data.accountId);
}
return new Response("ok"); // any 2xx = delivered
}Offline verification (optional)
Codes are 20 Crockford base32 characters: a 60-bit payload (version, deal id, term, issue day, serial) plus a 40-bit HMAC tag. With your deal's verification secret you can confirm a code is genuine without a network call:
import { createHash, createHmac } from "node:crypto";
// Optional: verify codes without calling ANKY.AI (e.g. offline or at the edge).
// DEAL_SECRET = your deal's verification secret (hex) from ANKY.AI.
const ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
const TERMS = ["MONTHLY", "QUARTERLY", "SEMI_ANNUAL", "ANNUAL", "LIFETIME"];
export function verifyAnkyCode(input, dealSecretHex) {
const body = input.toUpperCase().replace(/^\s*ANKY[-\s]*/, "")
.replace(/[\s-]/g, "").replace(/[IL]/g, "1").replace(/O/g, "0");
if (!/^[0-9A-HJKMNP-TV-Z]{20}$/.test(body)) return null;
let v = 0n;
for (const ch of body) v = (v << 5n) | BigInt(ALPHABET.indexOf(ch));
const tag = v & 0xffffffffffn; // low 40 bits
const mask = BigInt("0x" + createHash("sha256")
.update("anky-code-mask:" + tag.toString(16)).digest("hex").slice(0, 16)) & ((1n << 60n) - 1n);
const payload = (v >> 40n) ^ mask; // high 60 bits, unmasked
if (payload >> 58n !== 1n) return null; // version 1
const buf = Buffer.alloc(8); buf.writeBigUInt64BE(payload);
const mac = createHmac("sha256", Buffer.from(dealSecretHex, "hex")).update(buf).digest();
if (BigInt("0x" + mac.subarray(0, 5).toString("hex")) !== tag) return null;
return {
dealCodeId: Number((payload >> 48n) & 1023n),
term: TERMS[Number((payload >> 45n) & 7n)],
issuedAt: new Date(Date.UTC(2025, 0, 1) + Number((payload >> 30n) & 32767n) * 86400000),
};
}
// Offline checks prove a code is genuine. Still call /redeem (or store the code
// with a UNIQUE constraint) so each code is used once, and handle revocations.Testing
- Your test partner key starts with
pk_test_. Test and live keys never mix. - Ask your ANKY.AI partner manager for test codes for each of your deals, including a stacked set and a revoked code.
- Test: verify → redeem → redeem the same code on a second account (expect 409) → receive
code.revokedand remove access. - For fixed-term deals, confirm your system downgrades at
accessEndsAt.
Go-live checklist
- Redeem page live at the URL on your deal, accepts ?code=
- Sign-in/sign-up required before redeeming
- verify + redeem called server-side with your live key
- Returned plan and accessEndsAt applied; fixed-term downgrade scheduled
- Stacking tested up to your stack limit
- Redeemed codes stored with a UNIQUE constraint
- Webhook endpoint verifies signatures and handles code.revoked
- Billing page shows “via ANKY.AI” and hides paid upsells for deal plans
Want to list a deal? Submit your tool and pick “Lifetime Deal” — we'll set up your deal, keys and webhook with you.