# Tanso > Tanso is an open-source (AGPL-3.0) billing, metering, and entitlement > platform for B2B AI products, built for teams that sell credits or usage > and whose inference costs are big enough that margin per customer is a > real question. You self-host it; there is no hosted version. Every metered event carries its cost (tokens, model, provider, cost amount) alongside what was billed for it — costs and revenue in one ledger, so margin per customer, per feature, per model falls out of the same system that bills. Enforcement is real-time: entitlement checks, usage caps, and credit limits apply at event ingestion, not at invoice time. Stripe is the optional payment adapter, not the source of truth. ## Where things live - [Source code](https://github.com/tansohq/tanso-oss): AGPL-3.0, full feature set - [Documentation](https://tanso.mintlify.app): quickstart, API reference, MCP setup - [TypeScript SDK](https://www.npmjs.com/package/@tansohq/sdk): official JS/TS client - [This site](https://tansohq.com): marketing, blog, and pricing research archive ## Quickstart (Docker is the only prerequisite) git clone https://github.com/tansohq/tanso-oss.git cd tanso-oss/deploy cp .env.example .env # set JWT_SECRET docker compose up -d --build ./setup.sh # seeds a test account, prints credentials - [Full walkthrough](https://tanso.mintlify.app/quickstart): step-by-step self-host guide ## For agents A self-hosted instance can expose an MCP server at /mcp (off by default, enabled by the operator in config). It uses the same API-key auth and account scoping as the REST API — no separate, weaker path for agents. Tools that spend money require an explicit confirmAction argument. - [MCP setup and tool catalog](https://tanso.mintlify.app/mcp): connection guide - [Agent integration guide](https://tansohq.com/AGENTS.md): REST loop + MCP config - [Agent auth guide](https://tansohq.com/docs/agent-auth.md): API keys, idempotency - [MCP tool reference](https://tansohq.com/api/llms.txt): all 34 tools with parameters There is no hosted API at tansohq.com or api.tansohq.com. All integration happens against your own instance. ## Core model Feature -> Plan -> Rule (pricing: flat, usage, graduated; optional credit backing) -> Customer (keyed by your IDs) -> Subscription -> Events (idempotent, cost-stamped) -> Entitlements (fail-closed) -> Invoices (Stripe-synced or settled via mark-paid). ## Integration loop 1. Check an entitlement before serving a request. 2. Serve the request. 3. Report a usage event after. - [Billing lifecycle walkthrough](https://tanso.mintlify.app/billing-lifecycle): the loop in detail ## More context - [Developer resources](https://tansohq.com/developers/llms.txt): APIs, SDK, discovery files - [Pricing](https://tansohq.com/pricing.md): free, AGPL-3.0, self-hosted - [Glossary](https://tansohq.com/glossary.md): key terms - [Comparison vs Lago, Autumn, Stripe](https://tansohq.com/compare): honest side-by-side - [FAQ](https://tansohq.com/faq): common questions - [Integration skill](https://tansohq.com/skill.md): capabilities, inputs, constraints - [Full context file](https://tansohq.com/llms-full.txt): everything above in one document ## Free tools - [AI Pricing Finder](https://tansohq.com/ai-pricing-finder): how 80+ AI companies price, side by side - [Margin Analyzer](https://tansohq.com/margin-analyzer): which customers are profitable; CSV stays in the browser ## Blog - [PLG for the Agent Era, Part 3: Onboarding Agents](https://tansohq.com/blog/onboarding-agents): agent signup, auth, purchasing - [PLG for the Agent Era, Part 2: Is Your Site Ready for AI?](https://tansohq.com/blog/is-your-site-ready-for-ai): agent-readiness benchmarks - [PLG for the Agent Era, Part 1: Your Next Customer Might Be an Agent](https://tansohq.com/blog/agents-choosing-software): agents shape vendor choice - [The Missing Layer in Your Billing Stack](https://tansohq.com/blog/missing-layer-billing-stack) - [AI Credits: Shortcut or Trap?](https://tansohq.com/blog/credits-shortcut-trap) - [Self-Serve Isn't Dead. It Just Hasn't Been Rebuilt Yet.](https://tansohq.com/blog/agent-guided-self-serve) - [Why Falling Costs Don't Mean Better Margins](https://tansohq.com/blog/falling-costs-margins) - [Is Outcome-Based Pricing Real, or Just Marketing Hype?](https://tansohq.com/blog/is-outcome-based-pricing-real) - [AI Pricing Under Real Usage](https://tansohq.com/blog/ai-pricing-next) - [Outcome-Based Pricing for AI](https://tansohq.com/blog/outcome-based-pricing) - [When More Customers Mean Bigger Losses](https://tansohq.com/blog/more-customers-bigger-losses) - [Pricing Is a Real Moat in AI SaaS](https://tansohq.com/blog/pricing-moat-ai-saas) - [Why Pricing Infrastructure Gets Hard Fast for AI Startups](https://tansohq.com/blog/pricing-infrastructure-complexity) - [What's Actually Different About AI Pricing](https://tansohq.com/blog/ai-pricing-differences) - [How 30 AI Companies Actually Price Their Products](https://tansohq.com/blog/ai-pricing-research) --- # Tanso — Agent Integration Guide ## What Tanso Is Tanso is an open-source (AGPL-3.0) monetization engine for B2B AI products: billing, usage metering, real-time entitlements, credit pools, and per-customer margin in one ledger. It is self-hosted — there is no hosted API at tansohq.com. - Source: https://github.com/tansohq/tanso-oss - Docs: https://tanso.mintlify.app - SDK: https://www.npmjs.com/package/@tansohq/sdk ## MCP Every Tanso instance ships an MCP server at `/mcp` (off by default; the operator enables it with two config flags). Connect your agent to the instance you're working with: ```json { "mcpServers": { "tanso": { "url": "https://YOUR-INSTANCE/mcp", "headers": { "X-API-Key": "sk_test_your_key" } } } } ``` Auth and scoping are identical to the REST API — an agent gets no separate, weaker path. Tools that spend money or make hard-to-reverse changes require an explicit `confirmAction: true` argument. Setup and the full tool catalog: https://tanso.mintlify.app/mcp ## REST integration loop 1. `GET /api/v1/client/entitlements/{customerReferenceId}/{featureKey}` — check before serving 2. Serve the request 3. `POST /api/v1/client/events` — report usage (idempotent via `eventIdempotencyKey`) Full walkthrough: https://tanso.mintlify.app/billing-lifecycle --- --- name: tanso-integration description: Integrate billing, metering, and real-time entitlements with a self-hosted Tanso instance. Check an entitlement before serving a request, report a usage event after. --- # Tanso Integration Skill Tanso is an open-source (AGPL-3.0) billing, metering, and entitlement engine for B2B AI products. It is self-hosted: every endpoint below lives on your own instance (YOUR-INSTANCE), never on tansohq.com. ## Capabilities - **Check entitlements in the request path.** Ask "can this customer use this feature?" and get allow/deny plus usage, limit, and remaining quota before any compute runs. - **Meter usage with cost attached.** Report idempotent usage events that carry tokens, model, provider, and cost amount next to what was billed. - **Manage credit pools.** Grant credits, enforce hard limits, roll over balances. An empty pool returns deny. - **Bill subscriptions and usage.** Plans, proration, invoices; optional Stripe sync for payment collection. - **Drive it all from an agent.** Every instance ships an MCP server at /mcp (off by default) with 34 tools; actions that spend money require an explicit confirmAction argument. ## Required inputs - A running Tanso instance (`docker compose up -d` from https://github.com/tansohq/tanso-oss — Docker is the only prerequisite). - An API key generated in that instance's dashboard (Settings > API Keys), passed as `X-API-Key: sk_live_*` or `Authorization: Bearer sk_live_*`. - Your own customer reference IDs and feature keys. ## Core calls ``` GET /api/v1/client/entitlements/{customerReferenceId}/{featureKey} # check before serving POST /api/v1/client/events # report usage after ``` Events are idempotent via `eventIdempotencyKey`; duplicate keys are rejected with a structured error. ## Constraints - There is no hosted API on tansohq.com; all calls go to your own instance. - Entitlements are fail-closed: no active subscription or empty credit pool means deny. - Sensitive MCP tools (cancel subscription, mark invoice paid, Stripe setup) require `confirmAction: true` or they return `confirmation_required` without acting. - API keys are tenant-scoped; you can only touch your own account's data. ## Documentation - [Quickstart](https://tanso.mintlify.app/quickstart) - [Billing lifecycle](https://tanso.mintlify.app/billing-lifecycle) - [MCP setup and tool catalog](https://tanso.mintlify.app/mcp) - [Agent auth guide](https://tansohq.com/docs/agent-auth.md) - [Full MCP tool reference](https://tansohq.com/api/llms.txt) - [LLM context](https://tansohq.com/llms.txt) --- # Tanso — Agent Authentication Guide How AI agents and automated systems authenticate with a Tanso instance. Tanso is self-hosted. There is no hosted API at tansohq.com — every request goes to an instance you (or the operator you work with) run. Replace `YOUR-INSTANCE` below with that deployment's host. --- ## Client API The Tanso Client API uses API keys. Every request must include a key. ### Step 1: Get an API key 1. Open your instance's admin dashboard 2. Go to **Settings > API Keys** 3. Click **Generate API Key** 4. Copy the key. It starts with `sk_test_` or `sk_live_` If you don't have an instance yet, the quickstart takes a few minutes: https://tanso.mintlify.app/quickstart ### Step 2: Authenticate requests Pass your key in one of two ways: ``` Authorization: Bearer sk_test_your_api_key ``` or ``` X-API-Key: sk_test_your_api_key ``` ### Step 3: Verify ```bash curl -H "Authorization: Bearer sk_test_your_api_key" \ https://YOUR-INSTANCE/api/v1/client/plans ``` A successful response returns `{"success": true, "data": [...]}`. ### Permissions API keys grant full access to the Client API (`/api/v1/client/**`). All keys are scoped to the account they were created in. Tenant isolation is enforced at the database level. ### Key management - Keys can be rotated from the dashboard at any time - Keys support expiration dates - Revoke a key instantly by deleting it in the dashboard - Both old and new keys work during rotation until the old one is deleted ### Error responses | Status | Meaning | |--------|---------| | `401` | Invalid or missing API key | | `403` | Key is valid but access is denied | | `409` | Duplicate event (idempotency key already used) | ### Idempotency For event ingestion and write operations, include an idempotency key to make retries safe: ```bash curl -X POST https://YOUR-INSTANCE/api/v1/client/events \ -H "Authorization: Bearer sk_test_your_api_key" \ -H "Content-Type: application/json" \ -H "X-Idempotency-Key: evt_unique_123" \ -d '{"eventName": "api_call", "occurredAt": "2026-01-15T14:30:00Z", "customerReferenceId": "cust_123", "usageUnits": 1}' ``` --- ## MCP Server (Model Context Protocol) Every Tanso instance can expose an MCP server at `/mcp`. It is off by default; the operator enables it in config. Same API key, same account scoping as the REST API — there is no separate, weaker path for agents. ### Configuration Add to your MCP client config (Claude Desktop, Cursor, etc.): ```json { "mcpServers": { "tanso": { "url": "https://YOUR-INSTANCE/mcp", "headers": { "X-API-Key": "sk_live_your_api_key_here" } } } } ``` No additional handshake or session management required. Each tool call is independently authenticated. Tools that spend money or make hard-to-reverse changes require an explicit `confirmAction: true` argument. Setup and the full tool catalog: https://tanso.mintlify.app/mcp --- ## Resources - [llms.txt](https://tansohq.com/llms.txt) — product overview - [AGENTS.md](https://tansohq.com/AGENTS.md) — agent integration guide - [Documentation](https://tanso.mintlify.app) — full docs - [Source](https://github.com/tansohq/tanso-oss) — AGPL-3.0 - [TypeScript SDK](https://www.npmjs.com/package/@tansohq/sdk) --- # Tanso MCP Integration > Tanso is an open-source (AGPL-3.0), self-hosted monetization engine: > plans, feature entitlements, usage metering, and billing in one API. > Stripe is the optional payment adapter, not the source of truth. Define > your plans and features once, then let Tanso enforce access, track > consumption, and generate invoices automatically. > > There is no hosted API at tansohq.com. Everything below runs against your > own instance. For the product overview, see the companion file: llms.txt --- ## Connection & Authentication Protocol: Streamable HTTP (MCP) Auth: API key via `X-API-Key` header (same key used for the REST API) Prerequisite: MCP must be enabled on the Tanso instance (off by default; the operator enables it in config, see https://tanso.mintlify.app/mcp) Endpoint: `https://YOUR-INSTANCE/mcp` ### Sample MCP client config ```json { "mcpServers": { "tanso": { "url": "https://YOUR-INSTANCE/mcp", "headers": { "X-API-Key": "sk_live_your_api_key_here" } } } } ``` The server identifies itself as `tanso-mcp`. No additional handshake or session management is required — each tool call is independently authenticated. --- ## Concepts Quick Reference | Concept | Description | |----------------|-----------------------------------------------------------------------------| | Feature | A single capability your product offers (e.g. `api_access`, `ai_messages`) | | Plan | A bundle of features with pricing (e.g. "Pro Tier — $29/mo") | | Customer | A billing entity identified by an external reference ID from your system | | Subscription | Links a customer to a plan; manages billing periods and entitlements | | Event | A usage record (e.g. "API call made") that drives metering and billing | | Entitlement | A customer's right to use a feature, derived from their active subscription | | Invoice | A billing document generated per cycle; statuses: PENDING, DUE, PAID, PAST_DUE, VOID | | Credit Pool | A prepaid balance a customer can draw down against for metered features | --- ## Tool Reference Tanso exposes 34 tools across 7 categories. They are grouped below by access level. ### Read-Only Tools (13) These tools retrieve data and have no side effects. #### Plans **listPlans** List all active plans with their pricing and linked features. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | *(none)* | — | — | — | **listFeatures** List all features defined in your account. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | *(none)* | — | — | — | **getFeature** Get details for a single feature by its unique key. | Parameter | Type | Required | Description | |------------|--------|----------|------------------------------------------------------| | featureKey | string | yes | The unique feature key, e.g. `api_access` | #### Customers **getCustomer** Get customer details by their external reference ID. Returns name, email, active subscriptions, and credit pools. | Parameter | Type | Required | Description | |---------------------|--------|----------|----------------------------------------------------------| | customerReferenceId | string | yes | The customer's external reference ID (your system's ID) | #### Entitlements **checkEntitlement** Check if a customer has access to a specific feature. Returns whether access is allowed, the usage limit, current usage, and remaining quota. | Parameter | Type | Required | Description | |---------------------|--------|----------|----------------------------------------------------------| | customerReferenceId | string | yes | The customer's external reference ID | | featureKey | string | yes | The feature key to check, e.g. `api_access` | **listCustomerEntitlements** List all entitlements for a customer. Returns every feature the customer has access to, along with usage limits and current consumption. | Parameter | Type | Required | Description | |---------------------|--------|----------|----------------------------------------------------------| | customerReferenceId | string | yes | The customer's external reference ID | **evaluateEntitlement** Simulate an entitlement check with optional usage context. Records a zero-usage audit event but does NOT consume actual usage. Use this to check if proposed usage would be allowed before ingesting a real event. | Parameter | Type | Required | Description | |---------------------|--------|----------|----------------------------------------------------------| | customerReferenceId | string | yes | The customer's external reference ID | | featureKey | string | yes | The feature key to evaluate | | requestedUsage | string | no | Number of usage units to simulate, e.g. `100` | #### Billing **listCustomerInvoices** List all invoices for a customer. Returns invoice dates, amounts, status (PENDING, DUE, PAID, PAST_DUE, VOID), and line items. | Parameter | Type | Required | Description | |---------------------|--------|----------|----------------------------------------------------------| | customerReferenceId | string | yes | The customer's external reference ID | #### Credits **getCreditPools** List all credit pools for a customer. Returns each pool's name, total credits, used credits, and remaining balance. | Parameter | Type | Required | Description | |---------------------|--------|----------|----------------------------------------------------------| | customerReferenceId | string | yes | The customer's external reference ID | **getCreditBalance** Get the balance of a specific credit pool. | Parameter | Type | Required | Description | |---------------------|--------|----------|----------------------------------------------------------| | customerReferenceId | string | yes | The customer's external reference ID | | poolId | string | yes | The credit pool ID | #### Stripe Integration **getStripeIntegrationStatus** Returns the current Stripe integration status for the account. Shows whether an API key is registered, whether a webhook exists, the current Stripe mode, and currency. Call this first to determine which setup steps remain. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | *(none)* | — | — | — | Returns: `{ hasApiKey, maskedApiKey, hasWebhook, maskedWebhookSecret, stripeMode, stripeEnabled, currency, setupComplete }` **discoverStripeData** Discovers existing data in the connected Stripe account. Lists products, customers, and subscriptions from Stripe and shows which items are already mapped to Tanso entities. Use this to preview what will be imported before running `importStripeData`. Pre-check: A Stripe API key must be registered. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | *(none)* | — | — | — | Returns: Lists of `DiscoveredProduct` (name, id, alreadyMapped), `DiscoveredCustomer` (name, email, id, alreadyMapped), and `DiscoveredSubscription` (id, customerId, productId, status, alreadyMapped). **getStripeImportStatus** Checks the status of a Stripe import job by its ID. Returns progress details including total, processed, and failed item counts. | Parameter | Type | Required | Description | |-----------|--------|----------|----------------------------------------------------------| | jobId | string | yes | The import job ID (UUID format) | --- ### Write Tools (4) These tools create or modify data. They have side effects noted in each description. #### Customers **createCustomer** Creates a new customer in your billing system. This permanently creates a customer record. | Parameter | Type | Required | Description | |--------------------------|--------|----------|----------------------------------------------------| | customerReferenceId | string | yes | A unique reference ID for this customer, e.g. `user_abc123` | | email | string | yes | Customer's email address | | firstName | string | no | Customer's first name | | lastName | string | no | Customer's last name | **updateCustomer** Updates an existing customer's details. Only provided fields are updated; omitted fields remain unchanged. | Parameter | Type | Required | Description | |---------------------|--------|----------|----------------------------------------------------------| | customerReferenceId | string | yes | The customer's external reference ID | | firstName | string | no | New first name | | lastName | string | no | New last name | | email | string | no | New email address | | phoneNumber | string | no | New phone number | #### Subscriptions **createSubscription** Creates a subscription for a customer on a specific plan. The plan must be in ACTIVE status. **Tanso mode (no Stripe):** Creates the subscription and generates the first invoice. For IN_ADVANCE plans, the subscription stays inactive until the invoice is marked paid. For IN_ARREARS plans, entitlements are granted immediately. **Stripe Integration + paid IN_ADVANCE plans:** Returns a `checkoutUrl` instead of creating the subscription. Redirect the customer to this URL to collect payment via Stripe Checkout. The subscription, invoice, entitlements, and credits are created automatically after Stripe confirms payment via webhook. Free plans ($0) skip checkout and activate immediately. **Stripe Integration + IN_ARREARS plans:** The subscription activates immediately and is synced to Stripe. Stripe invoices at the end of the billing period. | Parameter | Type | Required | Description | |---------------------|--------|----------|----------------------------------------------------------| | customerReferenceId | string | yes | The customer's external reference ID | | planId | string | yes | The plan ID to subscribe to (use `listPlans` to find IDs)| Returns: `{ subscription, invoice, metadata, checkoutUrl }` — `checkoutUrl` is only present for paid IN_ADVANCE plans in Stripe Integration mode. When `checkoutUrl` is present, `subscription` and `invoice` may be null (created after payment). #### Events **ingestEvent** Ingests a usage event for billing and metering. Records usage that affects billing calculations and entitlement limits. Events are idempotent — sending the same idempotency key twice will be rejected. In Stripe Integration mode, usage is also forwarded to Stripe Meters (credits are deducted first; only overage is sent to Stripe). | Parameter | Type | Required | Description | |---------------------|--------|----------|----------------------------------------------------------| | eventIdempotencyKey | string | yes | Unique key to prevent duplicates, e.g. `evt_abc123` | | eventName | string | yes | Name of the event, e.g. `api_call` | | occurredAt | string | yes | ISO-8601 timestamp, e.g. `2026-03-16T10:30:00Z` | | customerReferenceId | string | yes | The customer's external reference ID | | featureKey | string | yes | The feature key this event is tracked against | | usageUnits | string | no | Number of usage units consumed (defaults to `1`) | | model | string | no | AI model name, e.g. `gpt-4o`, `claude-3-opus` | | modelProvider | string | no | AI model provider, e.g. `openai`, `anthropic` | | costUnits | string | no | Cost-relevant quantity (e.g. token count). Falls back to `usageUnits` | | costAmount | string | no | Cost in currency, e.g. `0.05`. If omitted, calculated from plan rules or model lookup | | revenueAmount | string | no | Revenue in currency, e.g. `0.10`. If omitted, calculated from plan rules | --- ### Sensitive Tools (6) — require `confirmAction: true` These tools perform destructive or financially significant operations. You MUST pass `confirmAction: true` to execute them. If `confirmAction` is `false` or omitted, the tool returns a `confirmation_required` error without performing the action. #### Subscriptions **cancelSubscription** Cancels a customer's subscription. This will end the customer's access to plan features. | Parameter | Type | Required | Description | |---------------|---------|----------|----------------------------------------------------------| | subscriptionId| string | yes | The subscription ID to cancel | | cancelMode | string | no | `END_OF_PERIOD` (default, graceful) or `IMMEDIATE` | | confirmAction | boolean | yes | Must be `true` to execute | **changeSubscriptionPlan** Changes a customer's subscription to a different plan. Upgrades take effect immediately with prorated billing. Downgrades are scheduled for the end of the current billing period. | Parameter | Type | Required | Description | |---------------|---------|----------|----------------------------------------------------------| | subscriptionId| string | yes | The subscription ID to change | | newPlanId | string | yes | The new plan ID to switch to | | changeType | string | yes | `UPGRADE` (immediate) or `DOWNGRADE` (end of period) | | confirmAction | boolean | yes | Must be `true` to execute | #### Billing **markInvoicePaid** Marks an invoice as paid. Updates the invoice status and may trigger Stripe synchronization. Use this for manual payment reconciliation. | Parameter | Type | Required | Description | |---------------|---------|----------|----------------------------------------------------------| | invoiceId | string | yes | The invoice ID to mark as paid | | confirmAction | boolean | yes | Must be `true` to execute | #### Stripe Integration **registerStripeApiKey** Registers a Stripe API key for the account. If a key already exists, deletes the old one first. The key must start with `sk_test_` or `sk_live_`. | Parameter | Type | Required | Description | |---------------|---------|----------|----------------------------------------------------------| | stripeApiKey | string | yes | The Stripe secret API key (starts with `sk_test_` or `sk_live_`) | | confirmAction | boolean | yes | Must be `true` to execute | **setupStripeIntegration** Sets up the full Stripe integration: creates a webhook endpoint and switches mode to STRIPE_DRIVEN. Idempotent — skips webhook if already registered, skips mode switch if already STRIPE_DRIVEN. The webhook creation implicitly validates the API key (Stripe API call will fail if key is bad). Pre-check: an API key must already be registered. | Parameter | Type | Required | Description | |---------------|---------|----------|----------------------------------------------------------| | confirmAction | boolean | yes | Must be `true` to execute | Returns: `{ success, webhookCreated, modeChanged, stripeMode }` **importStripeData** Imports Stripe data by auto-creating Tanso Plans, Features, Customers, and Subscriptions from the connected Stripe account. Skips items already mapped. Pre-check: A Stripe API key must be registered. Warns if Stripe mode is not STRIPE_DRIVEN. | Parameter | Type | Required | Description | |---------------|---------|----------|----------------------------------------------------------| | confirmAction | boolean | yes | Must be `true` to execute | Returns: `{ jobId, status, totalItems, processedItems, failedItems, errorDetails? }` --- ### Admin Read-Only Tools (2) These tools retrieve catalog data for administrative purposes. #### Features **adminListFeatures** List all features in the account with their IDs, names, keys, descriptions, and enabled status. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | *(none)* | — | — | — | #### Plans **adminListPlans** List all plans in the account with their IDs, keys, names, prices, billing intervals, and statuses. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | *(none)* | — | — | — | --- ### Admin Write Tools (8) These tools create or modify catalog data. They have side effects noted in each description. #### Features **adminCreateFeature** Creates a new feature in the product catalog. | Parameter | Type | Required | Description | |-------------|--------|----------|--------------------------------------------------| | name | string | yes | Display name, e.g. `API Access` | | key | string | yes | Unique key, e.g. `api_access` | | description | string | no | Description of the feature | **adminUpdateFeature** Updates an existing feature. Only provided fields are changed. Constraint: The `key` must remain unique within the account. | Parameter | Type | Required | Description | |-------------|---------|----------|--------------------------------------------------| | featureId | string | yes | The feature ID (UUID) | | name | string | no | New display name | | key | string | no | New unique key | | description | string | no | New description | | isEnabled | boolean | no | Enable or disable the feature | #### Plans **adminCreatePlan** Creates a new plan in the product catalog. New plans start in DRAFT status. To activate, set status to ACTIVE via `adminUpdatePlan` (requires name, description, priceAmount, intervalMonths, and at least one linked feature). | Parameter | Type | Required | Description | |----------------|--------|----------|------------------------------------------------------| | key | string | yes | Unique key, e.g. `pro_tier` | | name | string | yes | Display name, e.g. `Pro Tier` | | priceAmount | string | yes | Base price amount, e.g. `99.00` | | description | string | no | Description of the plan | | intervalMonths | string | no | Billing interval in months (default: `1`) | | billingTiming | string | no | `IN_ADVANCE` (default) or `IN_ARREARS` | **adminUpdatePlan** Updates an existing plan. Only provided fields are changed. Constraints: Once a plan is ACTIVE, `key`, `priceAmount`, `intervalMonths`, and `billingTiming` are locked and cannot be changed. ARCHIVED plans only allow status changes (back to ACTIVE). All fields are mutable while the plan is in DRAFT. | Parameter | Type | Required | Description | |----------------|--------|----------|------------------------------------------------------| | planId | string | yes | The plan ID (UUID) | | key | string | no | New unique key | | name | string | no | New display name | | priceAmount | string | no | New base price amount | | description | string | no | New description | | intervalMonths | string | no | New billing interval in months | | billingTiming | string | no | `IN_ADVANCE` or `IN_ARREARS` | | status | string | no | `DRAFT`, `ACTIVE`, or `ARCHIVED`. Allowed transitions: DRAFT→ACTIVE (requires name, description, priceAmount, intervalMonths, ≥1 linked feature), ACTIVE→ARCHIVED, ARCHIVED→ACTIVE. | **adminGetPlanWithFeatures** Get a plan's full details including all linked features and credit allocations. | Parameter | Type | Required | Description | |-----------|--------|----------|------------------------------------------------------| | planId | string | yes | The plan ID (UUID) | #### Plan-Feature Rules **adminGetFeatureRule** Get the pricing/cost rule for a plan-feature link. | Parameter | Type | Required | Description | |-----------|--------|----------|------------------------------------------------------| | planId | string | yes | The plan ID (UUID) | | featureId | string | yes | The feature ID (UUID) | **adminLinkFeatureToPlan** Links a feature to a plan with optional pricing rules. The `valueJson` parameter accepts a JSON string with **nested** pricing and cost config. Omit `valueJson` entirely for a boolean (on/off) gate. **Format:** `{"pricing": {}, "cost": {}}` **Pricing models** (inside `"pricing"` key): *Flat-rate* (`model: "usage"`): | Field | Type | Required | Default | Description | |------------------|--------|----------|------------|------------------------------------------------| | `model` | string | yes | — | Must be `"usage"` | | `usage_unit_type`| string | yes | — | Unit label, e.g. `"api_calls"`, `"messages"` | | `price_per_unit` | number | yes | — | Price charged per usage unit | | `max_usage` | number | no | *(none)* | Usage cap; must be ≥ 0 | | `reset_mode` | string | no | `"reset"` | `"reset"` (zero each cycle) or `"accumulate"` (carry over) | *Graduated / Tiered* (`model: "graduated"`): | Field | Type | Required | Default | Description | |------------------|--------|----------|------------|------------------------------------------------| | `model` | string | yes | — | Must be `"graduated"` | | `usage_unit_type`| string | yes | — | Unit label, e.g. `"api_calls"` | | `tiers` | array | yes | — | At least one tier object (see below) | | `max_usage` | number | no | *(none)* | Usage cap; must be ≥ 0 | | `reset_mode` | string | no | `"reset"` | `"reset"` or `"accumulate"` | Each tier object: | Field | Type | Required | Description | |------------------|----------------|----------|------------------------------------------| | `up_to` | number or `"inf"` | yes | Upper bound of this tier | | `price_per_unit` | number | yes | Price per unit in this tier; must be ≥ 0 | | `flat_fee` | number | no | Fixed fee added when this tier is entered; must be ≥ 0 | **Cost models** (inside `"cost"` key, optional — for internal margin analysis): *Simple* (`model: "simple"`): | Field | Type | Required | Default | Description | |------------------|--------|----------|--------------|------------------------------------------| | `model` | string | yes | — | Must be `"simple"` | | `cost_per_unit` | number | yes | — | Internal cost per usage unit; must be ≥ 0| | `cost_unit` | string | no | `"CURRENCY"` | `"TOKENS"`, `"CREDITS"`, or `"CURRENCY"` | *Model-aware* (`model: "model_aware"`): | Field | Type | Required | Default | Description | |-----------------------|--------|----------|--------------|----------------------------------------------------| | `model` | string | yes | — | Must be `"model_aware"` | | `default_cost_per_unit`| number| yes | — | Fallback cost per unit when model is not in map | | `model_costs` | object | yes | — | Map of model name → cost per unit, e.g. `{"gpt-4": 0.00006}` | | `cost_unit` | string | no | `"CURRENCY"` | `"TOKENS"`, `"CREDITS"`, or `"CURRENCY"` | **Examples:** - Boolean gate: omit `valueJson` - Flat-rate with cap: `{"pricing":{"model":"usage","price_per_unit":0.10,"usage_unit_type":"api_calls","max_usage":10000}}` - Graduated with flat fees and accumulation: `{"pricing":{"model":"graduated","usage_unit_type":"api_calls","reset_mode":"accumulate","tiers":[{"up_to":100,"price_per_unit":0.10,"flat_fee":5.00},{"up_to":"inf","price_per_unit":0.05}]}}` - Flat-rate with simple cost: `{"pricing":{"model":"usage","price_per_unit":0.10,"usage_unit_type":"api_calls"},"cost":{"model":"simple","cost_per_unit":0.03,"cost_unit":"CURRENCY"}}` - Flat-rate with model-aware cost: `{"pricing":{"model":"usage","price_per_unit":0.01,"usage_unit_type":"api_calls"},"cost":{"model":"model_aware","default_cost_per_unit":0.00003,"model_costs":{"gpt-4":0.00006,"claude-3-opus":0.000075},"cost_unit":"CURRENCY"}}` > Legacy flat format (cost fields mixed into the pricing object) is also accepted and normalized to nested format on save. | Parameter | Type | Required | Description | |-----------|---------|----------|------------------------------------------------------| | planId | string | yes | The plan ID (UUID) | | featureId | string | yes | The feature ID (UUID) | | type | string | no | Rule type. Must be `BASE`. Omit valueJson for a boolean (on/off) gate; provide valueJson with a pricing model for metered billing. | | valueJson | string | no | JSON string with pricing model config (see schemas above) | | isEnabled | boolean | no | Whether the rule is enabled (default: true) | **adminUpdateFeatureRule** Updates an existing plan-feature rule. Only provided fields are changed. | Parameter | Type | Required | Description | |-----------|---------|----------|------------------------------------------------------| | planId | string | yes | The plan ID (UUID) | | featureId | string | yes | The feature ID (UUID) | | type | string | no | Rule type. Must be `BASE` if provided. | | valueJson | string | no | JSON string with updated pricing model config | | isEnabled | boolean | no | Enable or disable the rule | --- ### Admin Sensitive Tools (1) — require `confirmAction: true` These tools perform destructive catalog operations. You MUST pass `confirmAction: true` to execute them. If `confirmAction` is `false` or omitted, the tool returns a `confirmation_required` error without performing the action. #### Plan-Feature Rules **adminUnlinkFeatureFromPlan** Removes a feature from a plan by deleting the plan-feature rule. | Parameter | Type | Required | Description | |---------------|---------|----------|--------------------------------------------------| | planId | string | yes | The plan ID (UUID) | | featureId | string | yes | The feature ID (UUID) | | confirmAction | boolean | yes | Must be `true` to execute | --- ## Confirmation Pattern Seven tools (`cancelSubscription`, `changeSubscriptionPlan`, `markInvoicePaid`, `adminUnlinkFeatureFromPlan`, `registerStripeApiKey`, `setupStripeIntegration`, `importStripeData`) require an explicit `confirmAction: true` parameter. This guard exists to prevent AI agents from accidentally performing irreversible financial, access-control, catalog, or integration operations. If you call a sensitive tool with `confirmAction: false` (or omit it), you will receive: ```json {"error": "confirmation_required", "message": "Set confirmAction to true to execute this destructive action"} ``` The action is NOT performed. Retry the same call with `confirmAction: true` to proceed. --- ## Common Agent Workflows ### Look up a customer's status 1. `getCustomer(customerReferenceId)` — retrieve profile, subscriptions, and credit pools 2. `checkEntitlement(customerReferenceId, featureKey)` — verify access to a specific feature 3. `listCustomerInvoices(customerReferenceId)` — review billing history ### Onboard a new customer 1. `listPlans()` — find the plan ID to subscribe the customer to 2. `createCustomer(customerReferenceId, email)` — register the customer 3. `createSubscription(customerReferenceId, planId)` — create the subscription 4. If the response contains `checkoutUrl` (Stripe + paid IN_ADVANCE): redirect customer to pay. The subscription is created automatically after payment. 5. If no `checkoutUrl` (Tanso mode or IN_ARREARS): the subscription is active. For Tanso mode IN_ADVANCE plans, call `markInvoicePaid` after collecting payment. ### Record usage 1. `checkEntitlement(customerReferenceId, featureKey)` — confirm the customer has quota remaining 2. `ingestEvent(eventIdempotencyKey, eventName, occurredAt, customerReferenceId, featureKey)` — record the usage ### Simulate usage before committing 1. `evaluateEntitlement(customerReferenceId, featureKey, requestedUsage)` — check if the proposed usage would be allowed 2. If allowed, proceed with `ingestEvent` ### Change or cancel a subscription 1. `getCustomer(customerReferenceId)` — confirm the customer and their current subscription 2. `changeSubscriptionPlan(subscriptionId, newPlanId, changeType, confirmAction: true)` — upgrade or downgrade 3. Or: `cancelSubscription(subscriptionId, cancelMode, confirmAction: true)` — cancel ### Check credit balance 1. `getCreditPools(customerReferenceId)` — list all pools and their balances 2. `getCreditBalance(customerReferenceId, poolId)` — drill into a specific pool ### Set up a product catalog 1. `adminCreateFeature(name, key)` — create each feature (repeat for all features) 2. `adminCreatePlan(key, name, priceAmount)` — create the plan (starts in DRAFT) 3. `adminLinkFeatureToPlan(planId, featureId, type: "BASE", valueJson)` — link each feature with pricing rules (repeat for all features) 4. `adminUpdatePlan(planId, status: "ACTIVE")` — activate the plan (requires name, description, priceAmount, intervalMonths, ≥1 linked feature) 5. `adminGetPlanWithFeatures(planId)` — verify the complete plan configuration ### Connect Stripe and import data 1. `getStripeIntegrationStatus()` — check current state (API key, webhook, mode) 2. `registerStripeApiKey(stripeApiKey, confirmAction: true)` — store the Stripe secret key 3. `setupStripeIntegration(confirmAction: true)` — create webhook + switch to STRIPE_DRIVEN mode 4. `discoverStripeData()` — preview products, customers, and subscriptions in Stripe 5. `importStripeData(confirmAction: true)` — auto-create Tanso entities from Stripe data 6. `getStripeImportStatus(jobId)` — verify the import completed successfully --- ## Error Handling All tools return JSON. On failure, the response shape is: ```json {"error": "", "message": ""} ``` | Error Type | Meaning | |-------------------------|------------------------------------------------------------| | `not_found` | The referenced customer, feature, subscription, or invoice does not exist | | `invalid_request` | A parameter is malformed or a business rule was violated | | `serialization_error` | Internal error serializing the response | | `confirmation_required` | A sensitive tool was called without `confirmAction: true` | | `precondition_failed` | A prerequisite is not met (e.g. no Stripe API key registered) | | `stripe_api_error` | A Stripe API call failed (invalid key, network error, etc.) | | `duplicate_event` | An event with the given idempotency key already exists | | `ingestion_failed` | The event could not be processed | On success, write tools return `{"success": true}` or `{"success": true, "message": "..."}`. Read tools return the requested data as JSON objects or arrays. --- # Tanso Pricing Tanso is open source under AGPL-3.0 and free. You self-host it; there is no hosted version, no paid tier, and no license-gated features. The full platform (entitlements, usage metering, subscription billing, credit pools, invoicing, Stripe sync, and the MCP server) is in the public repo. - Source: https://github.com/tansohq/tanso-oss - Docs: https://tanso.mintlify.app - Quickstart (Docker is the only prerequisite): https://tanso.mintlify.app/quickstart ## What running it costs Your own infrastructure: one JVM service and one Postgres, deployable with `docker compose up -d`. There is nothing to pay Tanso. ## FAQ **How much does Tanso cost?** Nothing. AGPL-3.0, full feature set, self-hosted. **Is there a hosted or paid version?** No. There is no hosted API at tansohq.com and no paid tier. **We already use Stripe. Why would we need Tanso?** Stripe handles payment collection. Tanso handles everything upstream: which customers get access to which features, how much they've consumed, whether they're within limits, what it cost you to deliver, and what your margin looks like. Tanso syncs invoices to Stripe automatically. Stripe is optional. **Can I change my customers' pricing without a code deploy?** Yes. Plans, pricing rules, usage limits, and credit grants are configured in your instance's dashboard. Changes take effect immediately. **What is a pre-flight entitlement check?** Before your app runs expensive compute (LLM inference, API calls, data processing), call the entitlement API. Tanso checks the customer's plan limits, credit balance, and subscription status. If they don't have capacity, the request is denied before you incur infrastructure costs. **How long does integration take?** Most teams are billing in a day. One API call for entitlement checks, one for usage events. ## Talk to us - Get a demo: https://cal.com/katrina-laszlo/30-minute-meeting - Product overview: https://tansohq.com/llms.txt - Agent integration: https://tansohq.com/AGENTS.md --- --- title: Glossary description: Key terms used in Tanso products and documentation last_updated: 2026-05-13 --- # Glossary ## Cost Attribution Linking AI compute costs (LLM API calls, GPU time) to the specific customer and feature that triggered them. ## Cost Alert A notification triggered when a customer's margin drops below a configured threshold. ## Customer Reference ID Your system's unique identifier for a customer, used to link Tanso data to your user records. ## Dimension A key/value pair that scopes entitlement rules and usage counters within a customer. Customer = org (billing entity); dimensions slice usage within that org (e.g., per API key, per user, per project). Rules without a dimension apply globally to the customer. ## Entitlement A runtime access check. Before executing a request, the system verifies the customer's plan grants access to the requested feature. Denied requests never hit compute. ## Entitlement Alert An alert triggered by entitlement consumption. Two types: `entitlement_usage_percent` (fires when usage exceeds a % threshold) and `entitlement_remaining` (fires when remaining units drop below a threshold). Requires a feature_key. ## Feature Key A unique string identifier for a billable capability (e.g., `ai-summarization`, `pdf-generation`). Used in entitlement checks and usage events. ## Gross Margin (Revenue - Cost of Goods Sold) / Revenue. In AI SaaS, COGS is dominated by model inference costs. ## MCP (Model Context Protocol) An open protocol for connecting AI agents to external tools and data sources. Tanso exposes billing and pricing tools via MCP servers. ## Metering Counting usage events (API calls, tokens, compute time) for billing and analytics. ## Usage Event A recorded instance of a customer using a feature. Contains: customer ID, feature key, timestamp, and optional properties (model, tokens, cost). ## Usage-Based Pricing Charging customers based on consumption rather than flat subscription fees. Common models: per-API-call, per-token, per-seat.