How to safely add paid AI features to a desktop app

By

Protect your AI API keys and margins with server-side authentication, per-user budgets, rate limits, and a global spending cap.

~~~

You sell a desktop application for $49.

The customer pays once, downloads it, and starts using it. But every time they click Generate, your application calls an AI model.

You get the bill for that call.

This changes the economics of desktop software. Selling the application does not end your financial obligation. A customer can keep generating tokens for months or years.

We need an architecture that puts a maximum price on that risk.

Why a lifetime license can become a liability

Traditional desktop software has a nice property. Once the customer downloads it, most of the work happens on their computer.

An AI feature changes this. Each request consumes a paid resource.

Suppose one generation costs you $0.04. A customer runs 10 generations across 5 projects every week:

10 generations × 5 projects = 50 generations/week
50 × 4.33 = 217 generations/month
217 × $0.04 = $8.68/month

At that rate, a $49 lifetime license pays for less than six months of inference. We have not included payment fees, hosting, support, taxes, or profit.

The useful equation is:

purchase price - lifetime AI cost = gross profit

The problem is the word lifetime. If AI usage has no limit, its lifetime cost has no limit either.

Never put your model API key in the application

The most dangerous architecture is also the easiest one to build:

Desktop app → AI provider

The application contains your Anthropic, OpenAI, or Google API key. It sends requests directly to the provider.

This creates two problems.

First, you cannot enforce a trustworthy usage limit. The customer controls the computer running your application. They can modify the application, replay requests, or bypass your interface.

Second, they can extract the API key. Obfuscating it makes extraction harder, but does not make the secret safe.

If you distribute a secret to customers, treat it as public.

Once someone has the key, they do not need your application anymore. They can call the provider directly while you pay the bill.

Put your server in the middle

The model provider should only accept requests from infrastructure you control.

The architecture becomes:

Desktop app

Your backend

AI Gateway

AI provider

The desktop application sends a license or session token to your backend. Your backend validates it and finds the customer account.

Only then does it send the AI request.

Your backend now decides:

The model API key stays in a server-side secret. It never reaches the desktop application.

If you want to build this layer on Cloudflare, a Worker is a good fit. My free Cloudflare course also covers Workers, AI Gateway, limits, and production operations.

I also have a complete Cloudflare AI Gateway guide covering setup, authentication, logging, caching, and model calls.

Do not trust a user ID from the client

The desktop application might send this:

{
  "userId": "customer_123",
  "prompt": "Summarize this project"
}

Do not use that userId for billing.

A customer can change it to customer_456. Your system would then assign their usage to someone else.

Instead, authenticate the request first:

Desktop app sends license token

Backend validates the token

Backend finds customer_123

Backend attaches trusted customer metadata

AI Gateway receives the request

The customer can send any userId they want. Your backend ignores it and derives the identity from the token.

This is the boundary that makes per-customer limits meaningful.

Add the trusted customer ID to each model request

Once your backend knows the customer, it can attach that identity to the AI Gateway request.

Cloudflare AI Gateway accepts custom metadata through the cf-aig-metadata header. Here is the important part of a Worker request:

const customer = await authenticateLicense(request, env)

const response = await fetch(env.AI_GATEWAY_URL, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${env.AI_GATEWAY_TOKEN}`,
    'Content-Type': 'application/json',
    'cf-aig-collect-log-payload': 'false',
    'cf-aig-metadata': JSON.stringify({
      user_id: customer.id,
      product: 'project-writer',
    }),
  },
  body: JSON.stringify({
    model: 'openai/gpt-5.5',
    messages: [
      {
        role: 'user',
        content: 'Summarize the current project',
      },
    ],
  }),
})

authenticateLicense() represents your own authentication or licensing system. It must reject invalid, expired, and revoked tokens.

AI_GATEWAY_URL contains your server-side gateway endpoint. The desktop application never receives that endpoint’s credentials.

The important detail is where customer.id comes from. The backend creates it after authentication. The desktop application does not choose it.

Do not include email addresses, prompts, license keys, or other secrets in metadata. Metadata appears in gateway logs.

Cloudflare Access provides another option. When a request reaches an Access-protected AI Gateway custom domain with a valid user identity, Cloudflare adds a trusted cf.user_id value. This can remove the need to pass your own user identifier.

For a consumer desktop application, I would usually keep my product authentication in the backend. It gives me direct control over licenses, plans, refunds, and credit purchases.

Use three separate limits

One limit cannot protect you from every failure.

I would add three layers.

Limit how quickly customers can send requests

A rate limit controls speed.

For example:

20 requests per customer per hour

This catches retry loops, automation, and obvious abuse. It also protects the rest of your backend.

Apply this limit using the authenticated customer ID, not only the IP address. Many customers can share an IP. One customer can also change networks.

An IP limit still helps as a second abuse signal. It should not be your customer identity.

Limit how much each customer can spend

A spending limit controls money.

For example:

$5 per customer per month

Cloudflare AI Gateway spend limits can split a rule by custom metadata such as user_id. Each customer then receives an independent budget.

When a customer reaches the limit, the gateway returns a 429 response until the budget window resets. Your application should translate that into a useful message:

You used this month's included AI allowance.
It resets on October 1, or you can add more credits now.

Do not show a generic network error. Reaching the allowance is a product state, not a broken connection.

Limit the entire product

Finally, add a global spending limit.

For example:

$500 across the entire application per month

This is the circuit breaker.

Imagine that a bug skips authentication. Or your customer metadata is missing. Or a new release enters a request loop.

The global limit prevents a product bug from becoming an unlimited bill.

Rate limits and spending limits solve different problems

A rate limit answers:

How quickly can this customer use the feature?

A spending limit answers:

What is the maximum this customer can cost me?

You need both.

Ten small requests and ten large requests can have very different prices. A customer can stay under a request limit while sending huge prompts and asking for long responses.

Tokens are closer to cost than request counts. Dollars are closer still.

I would also restrict input size and output tokens at the application layer. If the feature only needs a 500-word summary, do not accept a 2-million-token project and allow a 30,000-token response.

Know what a hard limit cannot guarantee

Cloudflare AI Gateway spend limits block requests when the tracked budget is exceeded. But cost tracking is an estimate based on token counts and known model prices.

The limits are also eventually consistent. The current request’s cost is recorded after it completes. Several concurrent requests can briefly take an account over its budget.

This means $5 per month should be treated as a strong control, not a mathematically exact ceiling.

Leave margin in your unit economics. If spending $5.20 instead of $5 would break the product, the product does not have enough margin.

Use the model provider’s billing controls as another boundary when it offers hard enforcement. Check the wording carefully. Some dashboards call a notification threshold a “budget,” but only send an alert after you cross it.

An alert is not a circuit breaker.

Decide what happens after the allowance is used

You have four useful pricing models.

Include a fixed number of generations

You can sell the application with a clear allowance:

$49 includes 500 AI generations

This is easy to explain, but generations do not all cost the same. You still need token and spending controls behind the scenes.

Include a dollar-based allowance

You can include a fixed inference budget:

$49 includes $5 of managed AI usage

This maps directly to your cost. Customers may find credits or generations easier to understand, so you can show friendly credits in the interface while tracking dollars internally.

Charge a subscription

A subscription aligns recurring revenue with recurring AI costs:

$19/month including up to $5 of AI usage

Your maximum inference cost resets with the customer’s payment. This makes the margin easier to model.

Let customers bring their own key

With bring your own key, or BYOK, the customer enters their provider key:

Customer → their AI account → their bill

Your application sells the workflow. The customer pays for inference.

BYOK works well for power users. It adds setup friction for everyone else and gives you less control over model availability.

I like the hybrid model:

This gives new customers a feature that works immediately. Heavy users do not create an unlimited cost.

Calculate the allowance before setting the price

Start with one complete operation.

A generation often includes more than one model call. You might classify input, retrieve context, generate a result, and validate it.

Measure the cost of the complete workflow:

input tokens
+ cached input tokens
+ output tokens
+ tool calls
+ retries
= cost per operation

Then estimate normal monthly usage:

cost per operation × operations per month
= expected AI cost per customer

Run the same calculation for a heavy customer. Averages do not protect you from the expensive end of the distribution.

If you want to compare models and workload assumptions, use my AI inference cost calculator. Put in your real input size, output size, calls per user, and active users.

Your allowance should come from this calculation. Do not choose $5 because it looks reasonable.

The architecture I would use

For a paid desktop application, I would start here:

Desktop application

Product authentication and license validation

Server-side API

Per-customer rate limit

AI Gateway with per-customer spend limit

AI provider

I would also add:

The desktop application would contain no unrestricted model credentials.

I would not build all of this for a local feature using a model that runs entirely on the customer’s computer. There is no paid inference account to protect in that case.

I would also avoid lifetime managed AI for an unpredictable workload. A subscription, prepaid credits, or BYOK gives the business a much safer shape.

Put a number on your worst case

AI usage is another cloud resource.

You would not give every customer unlimited access to your AWS account. Do not give them unlimited access to your AI budget.

Before shipping, you should be able to answer three questions:

What can one customer cost me this month?
What can all customers cost me this month?
What happens when either limit is reached?

If any answer is “unlimited,” the feature is not ready.

Tagged: AI · All topics

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about ai: