### Start OfferKit with Docker Compose Source: https://offerkit.dev/docs/self-host Copy the example environment file and start the stack. Required variables: DATABASE_URL, BETTER_AUTH_SECRET, ADMIN_EMAIL, ADMIN_PASSWORD. The web service runs migrations on first boot. ```bash cp .env.example .env # Required: # DATABASE_URL=postgres://offerkit:dev@postgres:5432/offerkit # BETTER_AUTH_SECRET=… # ADMIN_EMAIL=… # ADMIN_PASSWORD=… docker compose up -d ``` -------------------------------- ### Install TypeScript SDK Source: https://offerkit.dev/docs/reference/developer-tools Install the OfferKit SDK package using pnpm. ```bash pnpm add @offerkit/sdk ``` -------------------------------- ### Clone and Start OfferKit Locally Source: https://offerkit.dev/docs/deploy Commands to clone the repository, prepare the environment file, and start the services using Docker Compose. ```bash git clone https://github.com/offerkit/offerkit.git cd offerkit cp .env.example .env docker compose up -d ``` -------------------------------- ### Install and Use OfferKit CLI Source: https://offerkit.dev/docs/reference/developer-tools Install the CLI globally and perform authentication and resource operations. ```bash pnpm add -g @offerkit/cli offerkit login --url https://offerkit.example.com --api-key offerkit_… offerkit vouchers list offerkit vouchers validate WELCOME10 --amount 5000 offerkit vouchers redeem WELCOME10 --amount 5000 --idempotency-key order-42 offerkit customers list --search alice ``` -------------------------------- ### Pin a Release Version Source: https://offerkit.dev/docs/deploy Example of using a specific version tag for production deployments instead of the latest image. ```text ghcr.io/offerkit/offerkit:v0.2.0 ``` -------------------------------- ### GET /api/v1/ready Source: https://offerkit.dev/docs/reference/api-reference Checks readiness, including the database and worker expectations. Use this for the public web-service health check. ```APIDOC ## GET /api/v1/ready ### Description Checks readiness, including the database and worker expectations. Use this for the public web-service health check. ### Method GET ### Endpoint /api/v1/ready ### Response #### Success Response (200) - No specific response fields documented. #### Response Example No response example provided in the source. ``` -------------------------------- ### Metadata example Source: https://offerkit.dev/docs/integrate/customers-and-orders Prefer stable, documented fields with predictable types. Avoid copying sensitive data that rules do not need. ```json { "plan": "pro", "region": "AE", "lifecycle": "active", "marketingOptIn": true } ``` -------------------------------- ### Example webhook POST payload Source: https://offerkit.dev/docs/integrate/webhooks Each delivery is a JSON POST with fields such as `id`, `type`, `payload`, and `createdAt`. The `X-Offerkit-Signature` header contains the timestamp and HMAC signature for verification. ```json { "id": "event-uuid", "type": "voucher.redeemed", "payload": { "redemptionId": "redemption-uuid", "voucherCode": "WELCOME10", "amount": 1000 }, "createdAt": "2026-05-08T22:25:00Z" } ``` -------------------------------- ### Initialize TypeScript SDK Client Source: https://offerkit.dev/docs/reference/developer-tools Create a client instance using environment variables for the API URL and key. ```typescript import { createClient, verifyWebhook } from "@offerkit/sdk"; export const offerkit = createClient({ baseUrl: process.env.OFFERKIT_API_URL!, apiKey: process.env.OFFERKIT_API_KEY!, }); ``` -------------------------------- ### GET /api/v1/health Source: https://offerkit.dev/docs/reference/api-reference Checks process liveness for the OfferKit web service. ```APIDOC ## GET /api/v1/health ### Description Checks process liveness for the OfferKit web service. ### Method GET ### Endpoint /api/v1/health ### Response #### Success Response (200) - No specific response fields documented. #### Response Example No response example provided in the source. ``` -------------------------------- ### Initialize OfferKit Client Source: https://offerkit.dev/docs/referrals Server-side initialization of the OfferKit client using environment variables. This should only be performed on the server. ```typescript // lib/offerkit.ts import { createClient } from "@offerkit/sdk"; export const offerkit = createClient({ baseUrl: process.env.OFFERKIT_API_URL!, apiKey: process.env.OFFERKIT_API_KEY!, }); ``` -------------------------------- ### Worker Service Command Source: https://offerkit.dev/docs/deploy The command used to start the worker service in a production environment. ```bash node apps/worker/dist/index.js ``` -------------------------------- ### Configure OpenTelemetry Environment Variables Source: https://offerkit.dev/docs/self-host Set these environment variables to enable OpenTelemetry tracing and metrics. Replace with your actual Axiom token. ```text OTEL_EXPORTER_OTLP_ENDPOINT=https://api.axiom.co OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer ,X-Axiom-Dataset=offerkit ``` -------------------------------- ### GET /vouchers/{code}/transactions Source: https://offerkit.dev/docs/api-reference Gift card balance ledger lookup. Retrieves the transaction history for a voucher. ```APIDOC ## GET /vouchers/{code}/transactions ### Description Gift card balance ledger lookup. Retrieves the transaction history for a voucher. ### Method GET ### Endpoint /vouchers/{code}/transactions ### Parameters #### Path Parameters - **code** (string) - Required - The voucher code to look up. ### Response #### Success Response (200) - **data** (array) - List of transactions. - **next** (string) - Optional cursor for the next page. - **prev** (string) - Optional cursor for the previous page. #### Response Example { "data": [], "next": null, "prev": null } ``` -------------------------------- ### Build from source with dev compose file Source: https://offerkit.dev/docs/self-host For contributors testing local source changes, use the dev compose override to build the same image shape. ```bash docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build ``` -------------------------------- ### Configure Local MCP Stdio Process Source: https://offerkit.dev/docs/reference/developer-tools Use the published package for a local stdio process by adding it to the mcpServers configuration. ```json { "mcpServers": { "offerkit": { "command": "npx", "args": ["-y", "@offerkit/mcp"], "env": { "OFFERKIT_API_URL": "https://offerkit.example.com", "OFFERKIT_API_KEY": "offerkit_…" } } } } ``` -------------------------------- ### Get member balance and history Source: https://offerkit.dev/docs/solutions/loyalty Retrieves the current loyalty member status and transaction history. Use current.balance for spendable points and current.lifetimePoints for tier progress. ```javascript const current = await offerkit.loyalty.members.get({ params: { id: member.id } }); const history = await offerkit.loyalty.members.history({ params: { id: member.id } }); ``` -------------------------------- ### Required Environment Variables Source: https://offerkit.dev/docs/deploy Essential configuration variables for the OfferKit deployment. ```text DATABASE_URL=postgres://offerkit:dev@postgres:5432/offerkit REDIS_URL=redis://redis:6379 BETTER_AUTH_SECRET=replace-me ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=replace-me ``` -------------------------------- ### Enable MCP Server Source: https://offerkit.dev/docs/reference/developer-tools Configure environment variables to enable the OAuth-protected Streamable HTTP endpoint. ```bash OFFERKIT_MCP_ENABLED=true OFFERKIT_PUBLIC_URL=https://offerkit.example.com ``` -------------------------------- ### Recommended architecture diagram Source: https://offerkit.dev/docs/integrate ASCII diagram showing the recommended architecture: the customer UI communicates with your application server, which interacts with the OfferKit API. Only your server uses an API key, preventing public clients from accessing customer data or committing redemptions. ```text Customer UI │ ▼ Your application server ──────── OfferKit API │ │ ├── user and cart data ├── eligibility decisions ├── authoritative order ├── redemption and balance ledgers ├── payment state └── events and webhooks └── customer messaging │ ▲ ▼ └──────── webhook handler ────┘ ``` -------------------------------- ### Qualify automatic promotions Source: https://offerkit.dev/docs/integrate/checkout Calls the read-only promotions.qualify endpoint to get a combined preview after priority and exclusivity. Persist the selected tier IDs and amount with the order if the result is accepted. ```javascript const promotions = await offerkit.promotions.qualify({ customerId: customer.id, order: context.order, metadata: { channel: "web" }, filters: { includeSkipped: true }, }); ``` -------------------------------- ### Configure Codex MCP Source: https://offerkit.dev/docs/reference/developer-tools Add and authenticate the OfferKit MCP server within Codex. ```bash codex mcp add offerkit --url https://offerkit.example.com/mcp codex mcp login offerkit ``` -------------------------------- ### Create a gift card with the SDK Source: https://offerkit.dev/docs/solutions/gift-cards Creates a new gift card voucher within a GIFT_VOUCHERS campaign. The giftBalance is specified in minor units, where 5_000 represents USD 50.00. ```javascript const card = await offerkit.vouchers.create({ code: "GIFT-7K3PQ9LM", campaignId: giftCampaignId, type: "GIFT_CARD", giftBalance: 5_000, customerId: customer.id, }); ``` -------------------------------- ### Enable hosted MCP server Source: https://offerkit.dev/docs/self-host Set this variable on the web service to expose a Streamable HTTP MCP server at ${OFFERKIT_PUBLIC_URL}/mcp. Uses OAuth for client login. ```bash OFFERKIT_MCP_ENABLED=true ``` -------------------------------- ### Verify migration totals with SQL Source: https://offerkit.dev/docs/migrating-referrals Use this SQL query to compare the counts of legacy referral codes and conversions against the newly imported data in OfferKit. ```sql SELECT (SELECT COUNT(*) FROM legacy_referral) AS legacy_codes, (SELECT COUNT(*) FROM referral_code WHERE program_id = '') AS offerkit_codes, (SELECT COUNT(*) FROM legacy_conversion) AS legacy_conversions, (SELECT COUNT(*) FROM referral_conversion rc JOIN referral_code rcd ON rcd.id = rc.code_id WHERE rcd.program_id = '') AS offerkit_conversions; ``` -------------------------------- ### Set environment variables for local testing Source: https://offerkit.dev/docs/getting-started Configure the API URL and API key as environment variables for local development. Ensure the API key is stored securely and never exposed in client-side code. ```bash export OFFERKIT_API_URL=http://localhost:3000 export OFFERKIT_API_KEY=offerkit_… ``` -------------------------------- ### Configure Claude Code MCP Source: https://offerkit.dev/docs/reference/developer-tools Add the OfferKit MCP server to Claude Code. ```bash claude mcp add --transport http offerkit https://offerkit.example.com/mcp ``` -------------------------------- ### List customer vouchers with offerkit.vouchers.list Source: https://offerkit.dev/docs/referrals Retrieve a list of vouchers held by a customer, including gift cards, loyalty rewards, and manual coupons. ```javascript const { data: vouchers } = await offerkit.vouchers.list({ customerId: customer.id, limit: 50, }); // vouchers[i].code, .type ("DISCOUNT" | "GIFT_CARD"), .discount, .giftBalance, // .redemptionLimit, .redemptionCount, .expiresAt, … ``` -------------------------------- ### Create OfferKit order record Source: https://offerkit.dev/docs/integrate/customers-and-orders Create an order when operators should search orders in the OfferKit dashboard, inspect their lifecycle, or see attached redemptions. Pass offerkitOrder.id as orderId when redeeming. OfferKit order states are CREATED, PAID, FULFILLED, and CANCELED. ```javascript const offerkitOrder = await offerkit.orders.create({ externalId: order.id, customerId: customer.id, items: order.items.map((item) => ({ productId: item.productId, sku: item.sku, name: item.name, quantity: item.quantity, unitPrice: item.unitPriceCents, })), amount: order.subtotalCents, discountAmount: order.discountCents, currency: order.currency, status: "CREATED", }); ``` -------------------------------- ### Implement conversion in payment webhook Source: https://offerkit.dev/docs/referrals Execute the referral conversion after payment capture to ensure rewards are only issued for completed orders. ```javascript // In your payment webhook handler on("payment.succeeded", async (event) => { const order = await db.orders.findOrThrow(event.orderId); if (!order.referralCode) return; await offerkit.referrals.convert({ code: order.referralCode, refereeCustomerId: order.customerId, conversionEventId: order.id, }); }); ``` -------------------------------- ### Pin a release with OFFERKIT_IMAGE_TAG Source: https://offerkit.dev/docs/self-host Set OFFERKIT_IMAGE_TAG to a specific version, e.g., v0.2.0. Image channels: edge, latest, and stable versions. ```bash OFFERKIT_IMAGE_TAG=v0.2.0 docker compose up -d ``` -------------------------------- ### Convert a referral after success Source: https://offerkit.dev/docs/solutions/referrals Execute a conversion on your server after a successful event like a payment. Always provide a stable conversionEventId to ensure idempotency and prevent duplicate rewards. ```javascript const conversion = await offerkit.referrals.convert({ code: order.referralCode, refereeCustomerId: buyer.id, conversionEventId: order.id, }); if (!conversion.ok) { return handleReferralFailure(conversion.errorCode, conversion.message); } ``` -------------------------------- ### Configure Referral Program Rewards Source: https://offerkit.dev/docs/referrals JSON structure for defining referrer and referee rewards. Amounts are specified in the currency's minor unit. ```json { "referrerReward": { "kind": "discount", "discount": { "type": "AMOUNT", "amount": 2500 } }, "refereeReward": { "kind": "discount", "discount": { "type": "AMOUNT", "amount": 2500 } } } ``` -------------------------------- ### Upsert customer data with metadata Source: https://offerkit.dev/docs/solutions/targeting Use stable external IDs and consistent metadata fields to prepare customer data for targeting rules. ```javascript await offerkit.customers.upsert({ externalId: user.id, email: user.email, metadata: { vip: user.plan === "vip", region: user.region, acquisitionChannel: user.acquisitionChannel, }, }); ``` -------------------------------- ### Generate new referral codes via API Source: https://offerkit.dev/docs/migrating-referrals Calls referrals.issue for each legacy referrer, letting OfferKit mint new codes. Simpler and idempotent, but discards existing codes. ```typescript for (const referrer of legacyReferrers) { const customerId = externalToCustomerId.get(referrer.userId); if (!customerId) continue; await offerkit.referrals.issue({ programId: process.env.OFFERKIT_REFERRAL_PROGRAM_ID!, referrerCustomerId: customerId, }); } ``` -------------------------------- ### Implement Voucher Validation and Redemption in TypeScript Source: https://offerkit.dev/docs/getting-started Initialize the OfferKit client and define a function to validate and redeem a voucher. Validation should occur while the cart changes, while redemption should be reserved for specific business events like order creation. ```typescript import { createClient } from "@offerkit/sdk"; const offerkit = createClient({ baseUrl: process.env.OFFERKIT_API_URL!, apiKey: process.env.OFFERKIT_API_KEY!, }); export async function applyWelcomeCode(order: { id: string; totalCents: number; currency: string; }) { const validation = await offerkit.vouchers.validate({ params: { code: "WELCOME10" }, body: { order: { amount: order.totalCents, currency: order.currency, items: [] }, }, }); if (!validation.valid) return validation; return offerkit.vouchers.redeem({ params: { code: "WELCOME10" }, body: { order: { amount: order.totalCents, currency: order.currency, items: [] }, externalOrderId: order.id, idempotencyKey: order.id, }, }); } ``` -------------------------------- ### POST /api/v1/webhooks Source: https://offerkit.dev/docs/integrate/webhooks Creates a webhook endpoint in the OfferKit dashboard or via API. You can subscribe to all events or specify a list of event types. ```APIDOC ## POST /api/v1/webhooks ### Description Creates a webhook endpoint to receive OfferKit events. You can select `*` for every event or list the event types the handler understands. ### Method POST ### Endpoint /api/v1/webhooks ### Parameters #### Request Body - **eventTypes** (array of strings) - Optional - List of event types to subscribe to. Use `*` for all events. ### Request Example { "eventTypes": ["voucher.redeemed", "customer.created"] } ### Response #### Success Response (200) - **id** (string) - The webhook endpoint ID. - **secret** (string) - The plaintext signing secret, shown only once. #### Response Example { "id": "wh_123", "secret": "whsec_..." } ``` -------------------------------- ### Configure OpenTelemetry Export Source: https://offerkit.dev/docs/deploy Environment variables required to enable OpenTelemetry data export to a collector. ```text OTEL_EXPORTER_OTLP_ENDPOINT=https://your-collector OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer ``` -------------------------------- ### Import customers with externalId Source: https://offerkit.dev/docs/migrating-referrals Upserts legacy users as OfferKit customers, using externalId for idempotency. Safe to re-run; response includes { customer, created }. ```typescript import { createClient } from "@offerkit/sdk"; const offerkit = createClient({ baseUrl: process.env.OFFERKIT_API_URL!, apiKey: process.env.OFFERKIT_API_KEY!, }); for (const user of legacyUsers) { await offerkit.customers.upsert({ externalId: user.id, // your stable user id email: user.email, name: user.name, }); } ``` -------------------------------- ### Verify webhook signature with TypeScript SDK Source: https://offerkit.dev/docs/integrate/webhooks Use `verifyWebhook` from `@offerkit/sdk` for constant-time verification. Pass the raw body, signature header, and secret. It rejects old timestamps by default. Return a 401 on failure, otherwise process and return 204. ```typescript import { verifyWebhook } from "@offerkit/sdk"; export async function handleOfferKitWebhook(request: Request) { const rawBody = await request.text(); const signature = request.headers.get("x-offerkit-signature") ?? ""; if (!verifyWebhook(rawBody, signature, process.env.OFFERKIT_WEBHOOK_SECRET!)) { return new Response("Invalid signature", { status: 401 }); } const event = JSON.parse(rawBody); await processOnce(event.id, event); return new Response(null, { status: 204 }); } ```