Skip to main content

Services & Items

Services and items are the building blocks of your billing configuration. Understanding the difference is key to setting up pricing correctly.

What Is a Service?​

A service represents something your SaaS business consumes and pays for, or something you offer to your end users and charge them for. It's the fundamental billing category — every usage event belongs to exactly one service.

Services can represent virtually anything:

  • AI models — LLM tokens, image generation, speech-to-text
  • Cloud infrastructure — compute hours, storage GB, bandwidth
  • Communications — SMS messages, voice minutes, email sends
  • Platform resources — API calls, active users, data processing jobs

Examples:

ServiceMeasurement UnitWhat It Tracks
LLM TokenstokensLanguage model API consumption
Cloud ComputevCPU-hoursServer time used by customer workloads
Cloud StorageGB-monthsData stored on your platform
SMS VerificationmessagesSMS messages sent for user verification
Image GenerationimagesAI-generated images delivered to end users

Each service has:

PropertyDescriptionExample
NameDisplay name — editable any time, purely cosmetic. Renaming a service never affects API calls, charging, or historical usage records."LLM Tokens"
CodeAlphanumeric identifier used in API calls and data structures. Letters, digits, dashes (-), and underscores (_) only. Max 32 characters. Immutable once created — this is what your integration actually charges against.AI-TOKENS
Measurement unitThe raw unit your system reports (see below)tokens
Billing unitThe customer-facing unit used for pricing and invoices (see below)per 1M tokens
Units per billing unitHow many measurement units make up one billing unit1,000,000
Vendor costWhether the service records your vendor cost — set on the ServiceNo vendor cost, Has vendor cost

Why Measurement and Billing Units Are Separate​

Your system tracks usage in small, precise units (individual tokens, bytes, seconds). But pricing and invoices need to be expressed in larger, human-readable units. The measurement unit is the raw unit; the billing unit is the customer-facing unit used for pricing.

Example — LLM Tokens:

Your AI inference engine reports every individual token consumed. But pricing at $0.000000002 per token is confusing and error-prone. Instead:

SettingValue
Measurement unittoken
Billing unit1M tokens
Units per billing unit1,000,000
Price per billing unit$2.00

A customer who uses 3,500,000 tokens is charged: 3.5 × $2.00 = $7.00. The service total on the invoice reads "3,500,000 token (3.5 1M tokens)": the raw count that matches your usage records, then the same usage in billing units.

Example — Cloud Storage:

SettingValue
Measurement unitkilobyte
Billing unitGB
Units per billing unit1,048,576
Price per billing unit$0.023

A customer storing 512 MB is charged 0.5 × $0.023 = $0.0115. The rounding happens at the measurement unit level (kilobyte), so you're precise without billing fractions of a byte.

The key benefits:

  • Readable pricing: "$2.00 per 1M tokens" instead of "$0.000002 per token"
  • Clear invoices: Each service total shows the raw count and, when one billing unit holds several measurement units, the same usage in billing units, for example "3,500,000 token (3.5 1M tokens)"
  • Precision control: The measurement unit defines the smallest billable increment — so a 500-token request is billed for exactly 500 tokens, not rounded up to the next million

Vendor Cost​

Every service is billable via Plans — each Plan that includes a service sets its own per-unit customer price. On top of that, you decide whether the service should also record your vendor cost so PortaAIM can show you margin in analytics. This is the This service has a vendor cost switch you set when creating a service:

No vendor cost​

You set the customer-facing price in each Plan. No vendor cost is recorded on the Service, so its cost reads as zero in your margin figures and its revenue counts there as all margin — the price is whatever value-based number makes sense for the customer's experience.

Example: Your SaaS offers AI image generation. The customer is charged $0.10 per image. Under the hood you spend money on LLM tokens, GPU compute, and storage, but you don't need PortaAIM to track that cost-per-image: you handle the cost analytics elsewhere or simply accept the cost as part of the subscription margin.

  • Best for: proprietary features, value-based pricing, services where the customer-facing unit doesn't map 1:1 to your cost
  • What the customer sees: The per-unit price the Plan defines
  • What you see: Revenue per service

Has vendor cost​

The service is still billed via Plans (the Plan sets the customer price), and PortaAIM records your vendor cost per unit so you can see margin in analytics.

Example: You use OpenAI's GPT-4 API at $2.00/1M tokens and sell it to customers at $5.00/1M tokens. You create the service with Has vendor cost selected and set the default cost to $2.00. Then in each Plan you set the customer price to $5.00 — your markup is the difference, and analytics show $3.00/1M profit per transaction.

  • Best for: reselling third-party APIs, infrastructure-backed services where you know the vendor cost per unit and want margin visibility
  • What the customer sees: The Plan's per-unit price
  • What you see: Cost, revenue, and margin per service

How customer pricing and markup work​

In both cases, the per-unit customer price is set at the Plan layer, not on the service. The service defines the unit of measurement; the Plan decides how much each unit costs the customer.

That means a single service can be sold at completely different prices in different Plans:

  • Basic Plan charges $5.00 per 1M tokens
  • Pro Plan charges $3.00 per 1M tokens
  • Enterprise Plan charges $1.50 per 1M tokens, but only the first 10M are free

When the service has a vendor cost, markup is automatically derived for each Plan: markup = plan price − vendor cost. You don't pick a markup percentage — you pick a customer price, and the resulting markup falls out of the math.

Choosing the right setting​

ScenarioVendor costWhy
Reselling OpenAI/Anthropic API tokensHas vendor costDirect vendor cost maps to customer charge; margin matters
GPU compute resold per compute-hourHas vendor costYou know the cost and you bill it per unit
"Images generated" priced per imageNo vendor costCustomer pays per image; your costs are messy and you don't need them per-record
Premium support hoursNo vendor costYou set the price; no vendor cost to track
Outcome-based billing (e.g. $X per successful call)No vendor costThe customer price is value-based; underlying costs are tracked through separate services (see Outcome-Based Billing below)
Both modes together

A typical SaaS catalog uses both modes side by side. For example, an AI platform might have:

  • Has vendor cost: LLM Tokens (resold from OpenAI), GPU Compute (resold per minute)
  • No vendor cost: Image Generation (customer-facing per-image charge), Premium Support Hours (value-priced)

Pick Has vendor cost whenever you want a real margin per service in analytics. Pick No vendor cost when the customer price isn't a function of a single vendor cost: the service then costs nothing, so a plan prices it at a full 100% margin.

What Is an Item?​

Items are only needed when you have different pricing for different things within the same service. If everything in a service is priced the same, you don't need items at all.

When do you need items? When a single service contains multiple vendor models, tiers, or variants that should be priced differently.

Example — LLM Tokens service with Google Gemini models:

Google offers several Gemini models at different price points. You want to charge your customers accordingly:

ItemPrice per 1M TokensWhy Different
GEMINI-2.5-FLASH$0.50Fast, cheap model for simple tasks
GEMINI-3$2.00Standard model
GEMINI-3-PRO$5.00Most capable, highest cost

All three are "LLM Tokens" — same service, same measurement unit (tokens), same billing unit (1M tokens). But the price differs because the underlying models have different costs and capabilities.

When do you NOT need items? If you charge a flat rate regardless of the specific resource. For example, if all your speech-to-text models cost the same per minute, you just need the service — no items required.

Each item has:

PropertyDescriptionExample
NameDisplay name"Gemini 3 Pro"
IDAlphanumeric identifier used in API calls and charging requests. Letters, digits, dashes (-), and underscores (_) only. Max 16 characters.GEMINI-3-PRO
ScopeGlobal or scoped to a specific serviceService: LLM Tokens

Pricing an Item per Dimension​

AI vendors charge different rates for the tokens you send a model and the tokens it answers with, and one price cannot cover both. So each item carries a choice of how it is priced:

  • One price for all usage: a single rate covers everything charged against the item.
  • Separate prices per dimension: the item is priced separately for each kind of usage you name.

You name the dimensions. Input and output are filled in for you, and you can rename them, remove one or add more: a vendor that also charges for cached tokens is two more dimensions, CIN and COUT. A dimension is up to 4 letters or digits, and your integration reports it in capitals.

An item priced per dimension appears in the catalog as a single row with a badge per dimension, and it is priced once per dimension wherever prices are set, on the plan and on the service.

What your integration sends. Each dimension has its own charge code: the item ID, a dash, then the dimension in capitals.

DimensionCharge codeExample
INitem ID + -INOPUS46-IN
OUTitem ID + -OUTOPUS46-OUT
CINitem ID + -CINOPUS46-CIN

Charging requests carry the dimension code, never the item ID itself. The create form shows every code as you type.

The item ID is reserved even so. No other item may take it, and an item priced per dimension may not take an ID another item already holds, because the two would answer to one name and only one of them would open. Nothing is stored under that ID, so it looks free in the catalog; the create form tells you while you type.

The limits follow from that. A charge code fits in 16 characters, so an item ID used with dimensions is capped at 12 and a dimension at 4, and the two are checked together: a 12 character ID leaves room for CIN but not for COUT, and the form says so before you save. Such an item's name is capped at 55. How an item is priced is fixed when you create it: to price it the other way, create a new item.

You can add and remove dimensions later. Editing the item shows the set it carries, where you can add a dimension a vendor started charging for, or remove one you no longer sell. A dimension already in the set cannot be retyped, because its charge code is what your integration sends and what your issued invoices are filed under: to replace it, add the new one and remove the old. Four things to know before you save:

  • A dimension you have configured cannot be removed at all. The delete is greyed out once you have priced it in a plan or given it a vendor cost override, and a Prohibited row or a quota row with no price counts too. The billing backend keeps the pricing history and holds the code, so clearing the price does not make the dimension removable.
  • Removing one you have not configured deletes its charge code. Your integration has to stop reporting usage on it: those charges are then refused, or billed at whatever broader price matches. The code itself is freed, so you can use it again. Nothing is deleted until you save — the row stays struck through in the list, and you can keep it.
  • You can go down to one dimension, but not to none. An item priced per dimension has to start with two, because with one there is nothing to price separately, but an item you already have can be reduced to one and grown again later. To remove the last one, delete the item itself.
  • An added dimension has no price yet in plans that already sell the item. Until you set one, usage reported against its code is charged at whatever broader price matches. Add the price in each plan that sells the item.

Such an item is always edited as a whole. Renaming it, moving it to another service or deleting it applies to every dimension at once, and a single dimension cannot be changed on its own. Otherwise half of it could end up under a different service and disappear from its own row while still being billed. Moving it follows the rule every item follows: an item already under a service can only be released to Global from the edit form, and bound to the new service after that. Its own delete is greyed out as soon as one dimension has been configured, the same way that dimension's is.

Items you created as a pair by hand, such as GPT5-INPUT and GPT5-OUTPUT, keep working exactly as they are. They stay two separately priced items, because nothing guesses that two items belong together.

Item Scope​

Items can be:

  • Global: Available across all services — use for generic items like "Standard API Call"
  • Service-bound: Scoped to a specific service — use for vendor-specific items like "GEMINI-3-PRO" under a "Google AI" service

Scope decides where an item can be priced, not just where it is listed. An item can carry a price only on the service it is scoped to, or anywhere once it is global. When you configure item overrides for a service, in a plan or on the service itself, the picker offers that service's items and your global ones. Other items are not listed, apart from the per-dimension rule that follows, and the search box does not reach them, so make an item global when you want to price it on more than one service.

The billing backend enforces the same rule, so saving a price for an item scoped elsewhere is refused, and the message names the item and tells you to make it global. If an older plan or service already holds such a price, it cannot be saved again until you remove that row: the row says which service the item is scoped to, and its own delete control removes it.

With a large catalog the list shows the first matching items and tells you how many there are in total, so type into the search box to reach the rest.

An item priced per dimension is listed once, under its own name, with a badge for each dimension that has no override here yet. Adding it creates one override row for each of those, so you set the input price and the output price separately without hunting for two codes. If you only want to override one of them, delete the other row: an override row with no price typed in cannot be saved. Searching finds such an item by its name, by its ID, or by either dimension's charge code. After every dimension has an override here, the item stops appearing in the list. It is offered as soon as one of its dimensions is available for this service, and adding it brings its other dimensions with it.

How Services and Items Work Together​

Service: LLM Tokens (code: AI-TOKENS)
├── Item: GEMINI-2.5-FLASH → $0.50 per 1M tokens
├── Item: GEMINI-3 → $2.00 per 1M tokens
├── Item: GEMINI-3-PRO → $5.00 per 1M tokens
├── Item: GPT-4-TURBO → $3.00 per 1M tokens
└── Item: CLAUDE-OPUS → $4.00 per 1M tokens

Service: Image Generation (code: IMG-GEN)
├── Item: DALLE-3 → $0.04 per image
└── Item: STABLE-DIFFUSION → $0.02 per image

Service: Cloud Storage (code: STORAGE)
(no items — flat rate per GB regardless of storage type)

When your application sends a charge, it specifies both the service and the item:

{
"customer_external_id": "usr_9f3k2x",
"services": [
{
"service_code": "AI-TOKENS",
"item_code": "GPT-4-TURBO",
"units": 2500
}
]
}

The billing engine uses the item to look up the specific rate within the service's tariff.

Prefix Matching​

Item matching is broader than exact matching, but it works on whole dash-separated segments, not on any run of characters. The billing engine reads an item code as TYPE-SUBCODE-SUBCODE..., keeps the type together with the first subcode, and then drops one subcode at a time looking for a defined item. The most specific match wins.

Example: you define one item GPT-4 with a single price. Charges sent as GPT-4-TURBO or GPT-4-MINI both fall back to GPT-4 and are charged at that rate.

This is useful because:

  • Fewer pricing entries: you don't need a separate item and price for every model variant
  • Full detail preserved: the exact item code (e.g. GPT-4-TURBO) is stored in the usage record, so you can analyze which specific models are being consumed
  • Flexible granularity: if you later want different pricing for GPT-4-TURBO, just define that item, and it wins over GPT-4 for those charges
Items defined: Charges sent: Matched item:
├── GPT-4 ($4.00/1M) GPT-4-TURBO → GPT-4
│ GPT-4-MINI → GPT-4
├── GPT-4-TURBO ($6.00/1M) GPT-4-TURBO → GPT-4-TURBO
└── GEMINI-3 ($2.00/1M) GEMINI-35-PRO → no item, service price applies

Three rules decide whether one code falls back to another, and each of them surprises people:

  • Only whole segments count. GEMINI-35-PRO does not fall back to GEMINI-3, because 35 is a different segment from 3. Only GEMINI-35 would catch it.
  • The type alone is never a fallback. GPT-4-TURBO can fall back to GPT-4, but never to a bare GPT.
  • A dashed code is split only when it is upper-case. gpt-4-turbo or GPT_4-TURBO match nothing but themselves, so give your item codes and your charge codes the same upper-case, dash-separated shape. (A short code with no dashes, made only of digits or the letters A to D, is read as a dialled number and matched character by character instead. Avoid that shape for item codes.)
  • Long codes stop matching. Only the first 16 characters of a code are looked up, and a code whose type plus first subcode already exceeds that, such as TRANSCRIPTION-STANDARD, matches no item at all. Keep type codes short.

A charge that matches no item is billed at the service's own price, so a fallback you expected but did not get shows up as a pricing difference rather than an error.

Outcome-Based Billing​

Sometimes you want to charge for business outcomes rather than raw resource consumption. For example, a voice-AI provider might want to bill a customer $12 for every successful sales call completed by their agent — not for tokens, GPU seconds, or minutes spent on hold.

PortaAIM supports this through the same Services and Items building blocks:

Pattern​

  1. Create an outcome service with No vendor cost — this is the "what you bill the customer for" surface. Name it after the outcome (e.g. "Sales Calls", "Document Reviews", "Resolved Tickets").
  2. Define one item per outcome variant. Each item gets a fixed per-event price in the Plan — for example:
    • successful_sale → $12 per event
    • qualified_lead → $4 per event
    • no_answer → $0 per event (free outcome, still recorded)
  3. Send one charge per outcome from your application. Each units = 1 charge against the outcome item creates a revenue record at the configured outcome price.
  4. Track your real vendor costs separately by creating cost-tracked services for the resources that produce the outcome — LLM tokens consumed, voice minutes, storage, third-party API calls. Charge usage against those services as it happens (independent of whether the outcome was successful).

Why this works​

  • Revenue is tied to value, not consumption. The customer pays $12 the moment a sale lands, regardless of whether the agent burned 10 seconds or 10 minutes of compute getting there.
  • Margin per outcome is visible. Analytics show revenue (from the outcome service) and cost (from the underlying cost-tracked services) for the same customer over the same period — you can derive margin per outcome by dividing one by the other.
  • You can mix outcomes and consumption in the same Plan. A typical voice-agent Plan might bill $12 per successful_sale plus a subscription fee plus a per-minute charge for failed-but-billable calls — all at once.

Example: Voice AI sales agent​

ServiceVendor costUsed for
SALES_CALLSNo vendor costOutcome service — each item has a fixed customer price
LLM_TOKENSHas vendor costLLM cost per million tokens
VOICE_MINUTESHas vendor costTelephony cost per minute
STORAGEHas vendor costRecording storage cost

Items on SALES_CALLS: successful_sale ($12), qualified_lead ($4), no_answer ($0).

For one customer call that closes a sale and consumes ~50K tokens + 6 voice minutes + 0.5 MB storage, your application sends four charges:

  1. SALES_CALLS / successful_sale × 1
  2. LLM_TOKENS / gpt-4-turbo × 50,000
  3. VOICE_MINUTES / outbound-us × 6
  4. STORAGE / recordings × 0.5

The customer's invoice shows a single $12 line item for the outcome (plus the subscription fee), while your analytics layer sees the underlying cost trail.

Setting Up Services​

Every service is billable via Plans. The vendor-cost decision is additive — it only controls whether the service also records what you pay.

  1. Go to Product Catalog → Services tab
  2. Click Add Service
  3. Optionally pick a Template to pre-fill name and units
  4. Fill in Service Name, Slug, and Unit (required); enable Separate billing unit if invoiced units differ from measured units
  5. Set the This service has a vendor cost switch:
    • No vendor cost (default) → billable via Plans; no vendor cost is recorded.
    • Has vendor cost → enter the Default vendor cost; optionally add Per-item vendor cost overrides so individual items inside the service can have different vendor costs.
  6. Click Create Service
note

Every service is billable via Plans. To see a real margin per service in analytics, choose Has vendor cost; otherwise leave it on No vendor cost, and the service counts as costing nothing. See the Services guide for the full walkthrough.

Setting Up Items​

  1. Go to Product Catalog → Items tab
  2. Click Create Item
  3. Enter the item name and ID
  4. Choose scope: Global or select a specific service
  5. Save
tip

Create your services first, then create items scoped to those services. This keeps your catalog organized and ensures items appear in the right context when setting up pricing.