Docs Guides Build an AI API

Build an AI API with Holdify

Complete end-to-end tutorial: from Polar integration to feature-gated AI endpoints.

This tutorial walks you through building a monetized AI API like the one used by CodePilot. By the end, you'll have:

  • Custom plans with different quotas and features
  • Automatic subscription sync with Polar
  • API key verification with rate limiting and quota
  • Feature gating (e.g., only Pro users can use GPT-4o)
  • Per-customer usage tracking
1

Create a Holdify project

Sign up at app.holdify.io and create a new project. You'll get a project API key (hld_proj_live_xxx) that you'll use to authenticate your backend with Holdify.

Important: Keep your project API key secret. It should only be used in your backend, never exposed to clients.

2

Connect Polar

Go to Settings → Integrations → Polar in the Holdify dashboard. Click "Connect Polar" and authorize the integration. This enables automatic webhook sync.

When a customer subscribes via Polar, Holdify automatically:

  • Creates a tenant for the customer
  • Creates an entitlement with the correct plan
  • Sets quota and rate limits based on the plan
  • Updates entitlements when customers upgrade/downgrade/cancel
3

Create custom plans

Go to Settings → Plans and create your pricing tiers. Each plan defines:

  • Requests per month: The monthly quota limit
  • Rate limit: Maximum requests per minute
  • Features array: String identifiers for feature gating
Example plan configuration
yaml
// Example plan configuration in Holdify Dashboard
// Settings → Plans → Create Plan

Free Plan:
  - Name: "free"
  - Requests per month: 100
  - Rate limit: 10/minute
  - Features: []

Pro Plan:
  - Name: "pro"
  - Requests per month: 5000
  - Rate limit: 60/minute
  - Features: ["model:gpt-4o", "model:claude-sonnet"]

Business Plan:
  - Name: "business"
  - Requests per month: 25000
  - Rate limit: 300/minute
  - Features: ["model:gpt-4o", "model:claude-sonnet", "priority-queue"]

Map Polar products to plans

In the Polar integration settings, map each Polar product to a Holdify plan:

Product → Plan mapping
text
// In Holdify Dashboard: Settings → Integrations → Polar
// Map your Polar products to Holdify plans

Polar Product ID          →  Holdify Plan
────────────────────────────────────────────
prod_free_tier            →  free
prod_pro_monthly          →  pro
prod_pro_yearly           →  pro
prod_business_monthly     →  business
prod_business_yearly      →  business
4

Add verification to your backend

Install the SDK and add API key verification to your routes. The middleware should:

  • Extract the API key from the x-api-key header
  • Call Holdify /v1/verify to validate and get plan info
  • Check rate limits and quota, return 429/402 if exceeded
  • Gate features based on the features array
import { Hono } from 'hono';
import { Holdify } from '@holdify/sdk';

const app = new Hono();
const holdify = new Holdify({
  apiKey: process.env.HOLDIFY_PROJECT_KEY!,
});

"text-gray-500">// Middleware to verify API keys
app.use('/api/*', async (c, next) => {
  const apiKey = c.req.header('x-api-key');

  if (!apiKey) {
    return c.json({ error: 'API key required' }, 401);
  }

  const result = await holdify.verify(apiKey, {
    resource: 'chat_requests',
    units: 1,
    tenantId: apiKey.slice(-8), "text-gray-500">// Use last 8 chars as tenant ID
  });

  if (!result.valid) {
    return c.json({ error: 'Invalid API key' }, 401);
  }

  if (result.rateLimit.remaining <= 0) {
    return c.json(
      { error: 'Rate limit exceeded', retryAfter: result.rateLimit.reset },
      429
    );
  }

  if (result.quota.remaining <= 0) {
    return c.json(
      { error: 'Monthly quota exceeded', resetAt: result.quota.resetAt },
      402
    );
  }

  "text-gray-500">// Store result for route handlers
  c.set('holdify', result);
  await next();
});

"text-gray-500">// Chat endpoint with feature gating
app.post('/api/chat', async (c) => {
  const { model, messages } = await c.req.json();
  const holdifyResult = c.get('holdify');

  "text-gray-500">// Feature gating: check if user has access to the requested model
  const modelFeature = `model:${model}`;
  if (!holdifyResult.features.includes(modelFeature)) {
    return c.json({
      error: `Upgrade to access ${model}`,
      currentPlan: holdifyResult.plan,
      requiredFeature: modelFeature,
    }, 403);
  }

  "text-gray-500">// Route to AI provider based on model
  const response = await callAIProvider(model, messages);

  return c.json({
    response,
    usage: {
      quotaRemaining: holdifyResult.quota.remaining,
      rateLimitRemaining: holdifyResult.rateLimit.remaining,
    },
  });
});

export default app;
5

Understand the user flow

Here's how everything connects from user signup to API usage:

End-to-end flow
text
┌─────────────────────────────────────────────────────────────────┐
│                        User Flow                                 │
└─────────────────────────────────────────────────────────────────┘

1. User visits your pricing page
   └── yourapp.com/pricing

2. User clicks "Subscribe to Pro"
   └── Redirects to Polar checkout

3. User completes payment
   └── Polar processes payment

4. Polar sends webhook to Holdify
   └── POST https://api.holdify.io/webhooks/polar
   └── Holdify creates/updates entitlement automatically

5. User gets API key
   └── Option A: From your dashboard (you fetch from Holdify API)
   └── Option B: Auto-generated and emailed
   └── Option C: User creates via your UI → Holdify API

6. User makes API requests
   └── Your backend calls Holdify /v1/verify
   └── Holdify returns plan, features, quota
   └── You route to AI provider accordingly

7. Quota is tracked automatically
   └── Each verify call with units > 0 decrements quota
   └── When quota hits 0, verify returns quota.remaining = 0
6

How users get their API keys

After subscribing via Polar, users need an API key. You have several options:

Option A: Dashboard UI (Recommended)

Build a "API Keys" page in your customer dashboard. Use the Holdify API to list and create keys for the logged-in customer.

Option B: Auto-generate on subscription

Listen for Polar webhooks in your own backend, then automatically create a Holdify API key and email it to the customer.

Option C: CLI authentication

For CLI tools, implement OAuth or device flow. After auth, your backend creates/returns a Holdify API key.

Dashboard API endpoints
typescript
// Fetch customer's API key from your dashboard
// This is what you show in YOUR customer dashboard

// Backend endpoint to get customer's keys
app.get('/api/my-keys', async (req, res) => {
  const customerId = req.session.userId; // Your auth

  // Call Holdify API to get keys for this customer
  const response = await fetch(
    `https://api.holdify.io/v1/api-keys?tenantId=${customerId}`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.HOLDIFY_PROJECT_KEY}`,
      },
    }
  );

  const { keys } = await response.json();
  res.json({ keys });
});

// Create a new key for customer
app.post('/api/my-keys', async (req, res) => {
  const customerId = req.session.userId;

  const response = await fetch('https://api.holdify.io/v1/api-keys', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.HOLDIFY_PROJECT_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      tenantId: customerId,
      name: req.body.keyName || 'Default Key',
    }),
  });

  const key = await response.json();
  res.json({ key });
});
7

Test your integration

Test the full flow with curl:

Testing with curl
bash
# Test your API with a customer key
curl -X POST https://your-api.com/api/chat \
  -H "x-api-key: hld_live_customer_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

# Response for Pro user:
{
  "response": "Hello! How can I help you today?",
  "usage": {
    "quotaRemaining": 4999
  }
}

# Response for Free user trying to use GPT-4o:
{
  "error": "Upgrade to access gpt-4o",
  "currentPlan": "free"
}

You're all set!

Your AI API now has:

  • Automatic subscription sync with Polar
  • Plan-based quotas that reset monthly
  • Per-minute rate limiting
  • Feature gating for premium models
  • Per-customer usage tracking

Next steps