For software partners

Accept ANKY.AI deal codes on your product.

Sell your SaaS or AI tool as a lifetime, annual, semi-annual, quarterly or monthly deal. Buyers pay ANKY.AI, receive a redemption code, and activate it on your site — here's everything your team needs to build.

How deals work

  1. Step 1

    Buyer purchases

    On ANKY.AI, in INR or USD. ANKY.AI handles payment, GST invoice and refunds.

  2. Step 2

    Code is issued

    One code per unit bought, e.g. ANKY-7KQ2M-X4PZC-9WJ3T-HB8RD. Stacked codes = several units.

  3. Step 3

    Buyer redeems on your site

    They sign in or sign up on your product and enter the code on your redeem page.

  4. 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

  1. 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.
  2. 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.
  3. Verify, then redeem, from your server (never from the browser — your partner key is a secret). Call verify to show what the code unlocks, then redeem when the user confirms.
  4. Activate the returned plan. Use plan and accessEndsAt from the redeem response. Lifetime deals return null — the plan never expires. Fixed-term deals return an end date: downgrade the account then (don't auto-charge; offer your normal renewal).
  5. Support stacking if your deal allows more than one code: each additional code redeemed on the same account returns the higher tier in plan and the running stack.count.
  6. Store every redeemed code with a UNIQUE constraint, so a code can never activate two accounts even if two requests race.
  7. Handle revocations. Expose a webhook endpoint, verify the signature, and on code.revoked remove the access that code granted (and lower the stack tier if the account had several).
  8. 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

TermAccess after redemptionDefault redeem windowBuyer refund window
Monthly MONTHLY1 month30 days after purchase7 days
Quarterly QUARTERLY3 months60 days after purchase14 days
Semi-Annual SEMI_ANNUAL6 months90 days after purchase30 days
Annual ANNUAL12 months90 days after purchase30 days
Lifetime LIFETIMEForever (life of the product)365 days after purchase60 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

401unauthorizedMissing or wrong partner key.
404code_invalidNot a genuine code, or not one of your deals.
409already_redeemedCode was redeemed on a different account.
409stack_limitAccount already holds the maximum codes for this deal.
410code_revokedRefunded or revoked — don't activate.
410code_expiredNot 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.revoked and 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.