Discounts
Fixed or percentage codes with caps, product and collection eligibility, and limits. Generate up to 100,000 unique codes in one call.
vouchers.validate WELCOME10, USD 50.00 cartUSD 10.00 off · USD 40.00 dueOpen-source Voucherify alternative
OfferKit is an open-source promotion engine for teams that own their checkout. Integrate one API for coupons, gift cards, loyalty, and referrals. After that, your team changes offers from the dashboard.
git clone https://github.com/offerkit/offerkit
cd offerkit && cp .env.example .env
docker compose upThen open localhost:3000, mint an API key, and create WELCOME10. Quickstart steps →
// On your server, with an OfferKit API key
await offerkit.vouchers.validate({
params: { code: "WELCOME10" },
body: {
order: { amount: 5000, currency: "USD" },
},
});{
"valid": true,
"preview": {
"amount": 1000,
"finalOrder": {
"amount": 4000,
"currency": "USD"
}
}
}WELCOME10 takes a fixed USD 10.00 off. 5000 means USD 50.00, because amounts are integers in minor units.
// On your server, with an OfferKit API key
await offerkit.vouchers.validate({
params: { code: "WELCOME10" },
body: {
order: { amount: 5000, currency: "EUR" },
},
});{
"valid": false,
"code": "currency_mismatch",
"explanations": [
{
"code": "currency_mismatch",
"voucherCode": "WELCOME10"
}
]
}Rejections return a typed code your checkout can branch on, plus a readable message.
await offerkit.vouchers.stackRedeem({
codes: ["SUMMER15", "LOYAL5"],
order: { amount: 10000, currency: "USD" },
idempotencyKey: "order_5002",
});{
"ok": true,
"amount": 2000,
"finalOrder": { "amount": 8000 },
"breakdown": [
{ "code": "SUMMER15", "amount": 1500 },
{ "code": "LOYAL5", "amount": 500 }
]
}15% plus USD 5.00 off a USD 100.00 cart, committed in one transaction. If either code fails, neither is used.
Campaigns, customers, orders, and rules are shared by every incentive. Each result below came from a real call against a local instance.
Fixed or percentage codes with caps, product and collection eligibility, and limits. Generate up to 100,000 unique codes in one call.
vouchers.validate WELCOME10, USD 50.00 cartUSD 10.00 off · USD 40.00 dueStored balances you can spend in part. Every debit lands in an append-only transaction ledger.
vouchers.redeem on a USD 50.00 card, USD 18.50 orderUSD 18.50 spent · USD 31.50 leftPrograms with tiers, rewards, and a points ledger. Points can expire per earning.
loyalty.members.earn, 129 base points for order_5003+129 points · balance 129 · EARN ledger entryA stable code per advocate. Each conversion is recorded once and rewards both sides.
referrals.convert ANNA-UfXSjyQqUSD 10.00 voucher to each personAttach a JSON Logic rule to a campaign. This one limits VIPWEEKEND to VIP customers with a cart of USD 40.00 or more.
{
"and": [
{ ">=": [
{ "var": "order.amount" },
4000
] },
{ "===": [
{ "var": "customer.metadata.vip" },
true
] }
]
}The admin dashboard ships in the same image. Support sees every failed redemption grouped by its typed reason, and marketing adjusts the offer in the same place. Screens are from a local instance with demo data.


Checkouts retry, double-submit, and fail halfway. These are OfferKit's guarantees inside each call, and the one thing your application still owns.
Preview a code each time the cart changes. Counters, balances, and limits stay untouched.
POST /api/v1/vouchers/{code}/validateUse the order ID as the idempotency key. A retry returns the first result instead of redeeming twice.
idempotencyKey: order.idconst body = {
order: { amount: 5000, currency: "USD" },
idempotencyKey: "order_5001",
};
const redeem = () =>
offerkit.vouchers.redeem({
params: { code: "WELCOME10" },
body,
});
try {
await redeem();
} catch {
// Only transport errors throw. Rejections
// return { ok: false, code } instead.
await redeem();
}{
"ok": true,
"redemptionId": "b32060f3-46e9-…",
"amount": 1000,
"idempotent": true
}Apply up to 20 codes to one order in a single database transaction. All commit, or none do.
POST /api/v1/redemptions/stackSixteen typed codes, from expired vouchers to spent limits, each with a message support can read.
"code": "redemption_limit_reached"There is no public rollback endpoint, and cancelling an order does not reverse a redemption. Issue a replacement voucher, adjust loyalty points, or refund through your commerce system.
Recovery guide →The REST API, SDK, CLI, and MCP server share one typed contract. They accept the same inputs and return the same results.
Install with npm install @offerkit/sdk. The SDK, REST API, and CLI authenticate with a server API key from the dashboard. The hosted MCP server uses OAuth account sign-in.
Typed from the contract. Nothing to regenerate.
import { createClient } from "@offerkit/sdk";
const offerkit = createClient({
baseUrl: process.env.OFFERKIT_API_URL,
apiKey: process.env.OFFERKIT_API_KEY,
});
// In your order handler
await offerkit.vouchers.redeem({
params: { code: "WELCOME10" },
body: {
order: { amount: 5000, currency: "USD" },
idempotencyKey: order.id,
},
});Plain HTTP with an OpenAPI 3.1 document.
curl -X POST \
"$OFFERKIT_API_URL/api/v1/vouchers/WELCOME10/redemption" \
-H "Authorization: Bearer $OFFERKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order": { "amount": 5000, "currency": "USD" },
"idempotencyKey": "order_5001"
}'Back-office work from a terminal or script.
npm install -g @offerkit/cli
offerkit login --url https://offers.example.com \
--api-key offerkit_…
offerkit vouchers validate WELCOME10 \
--amount 5000
offerkit vouchers redeem WELCOME10 \
--amount 5000 --idempotency-key order_5001OAuth-protected tools, each marked safe, mutating, or destructive.
# On the OfferKit web service
OFFERKIT_MCP_ENABLED=true
# In Claude Code
claude mcp add --transport http offerkit \
https://offers.example.com/mcp
# In Codex
codex mcp add offerkit \
--url https://offers.example.com/mcp| Aspect | OfferKit | Voucherify |
|---|---|---|
| Price | Free. You pay for your own hosting. | From $650/month for 25,000 API calls (Business plan) |
| License | MIT. Read, fork, and patch the engine. | Proprietary |
| Hosting and data | Your infrastructure and your Postgres | Vendor cloud. Private hosting on Enterprise. |
| Operations | You run Postgres, Redis, and upgrades | Managed, with custom SLAs on Enterprise |
| Interfaces | REST, SDK, CLI, and a self-hosted MCP server, all from one contract | REST API and SDKs |
| Programs | Coupons, gift cards, loyalty, referrals | Those, plus bundling, gamification, and wallets |
| Usage limits | None. Throughput depends on your hardware. | 25,000 API calls a month and 100 a minute on Business |
| Maturity | Young project (v0.x), community-supported | Established commercial vendor |
Choose Voucherify for a managed platform with the widest feature set.
Choose OfferKit to own the code, the data, and the deployment, if you're comfortable running it.
There is no Voucherify importer yet. Campaigns, vouchers, customers, and validation rules are the same core concepts, so offers can be recreated through the API; see how objects connect. Voucherify figures are from its pricing page, checked November 2026.
Once docker compose up is running, add the SDK to your checkout server or try a code from the CLI. The quickstart walks through creating WELCOME10.
# In your checkout server
npm install @offerkit/sdk
# Or try the CLI against your instance
npm install -g @offerkit/cli
offerkit login --url http://localhost:3000 \
--api-key offerkit_…
offerkit vouchers validate WELCOME10 \
--amount 5000