Docs menu

Configure your server

Billing

Charge for hosted use with Stripe: an own-key plan, monthly model credits and top-ups.

Plans

Billing stays off unless STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET are both set (see .env.example). Enabling billing offers a monthly own-key plan for a personal model connection or monthly model credits in USD: $10, $20, $50, $100, $200, or $500. One-time top-ups use the same amounts. Monthly credits expire at the end of their service period; one-time top-ups do not expire. The hosted computer is included in both paths, and monthly credits include the own-key plan.

BYOK_MONTHLY_PRICE_CENTS in @orbin/contracts sets the own-key plan’s price; null makes it free and ends paid own-key subscriptions at their period end. Runs check access on the server, so a lapsed organization’s bots and routines stop until it subscribes.

Model credits

Configure the managed default model through the existing model connection settings. The default OpenRouter model is Luna 6; customers can choose another available model, with its usage deducted at model cost. In credit mode, the configured hosted provider uses Preá’s key even when a personal key for that provider is saved. Switching back to BYOK restores that connection. Personal connections to other providers continue to use their own keys and never debit Preá credits. Hosted calls reserve an upper cost estimate atomically before starting, then settle to reported or catalog-priced usage. Unknown usage retains its reservation for reconciliation.

STRIPE_PRICE_ID is optional and only needed to display the legacy seat price. Existing live seat subscriptions retain their access and billing portal. Upgrades charge the prorated difference immediately and add the amount collected to the credit balance; a failed payment leaves the plan unchanged. Downgrades lower the next renewal without charging now or removing credits. Credit purchases require settled payments; webhook replays do not add credits twice, and refunds reduce the remaining account balance.

Monthly funds are spent before top-ups. Each grant has an audited allocation lot, so reservations retain their funding source across renewal: unused monthly holds expire when released, while purchased top-ups remain available. Prorated upgrades share the original period end. Expiration creates a financial journal delta and runs before balance reads, reservations, settlements, and background recovery. Unclassified legacy balances are preserved as non-expiring funds.

Accounting

Postgres owns accounting. billing_audit_events is an append-only journal enforced by database triggers; credit entries, usage rows, and balances are projections. Each managed call shares one callId across reservation, durable provider receipt, usage, and settlement. Provider generation IDs, token buckets, cost provenance, and pricing snapshots are retained. Usage and settlement commit together, and journal/usage history survives clearing messages or deleting a bot or organization. Corrections append evidence rather than rewriting earlier journal events. Existing rows receive explicit baseline events when the migration runs; earlier history cannot be recovered.

The API/worker reconciler recovers saved receipts and, when supported by the configured provider, fetches authoritative generation costs for interrupted calls and estimates. Retries use leases and idempotent settlement. Unverifiable calls retain their holds and become needs_review after six attempts. An hourly, paginated audit rebuilds drifted balance caches from the journal and records repairs. Investigate Model credit hold requires review logs using the shared call ID; do not turn missing provider evidence into a zero-cost settlement.

OpenRouter inference requests include a keyed pseudonym in user, HTTP-Referer (https://prea.ia.br), and X-OpenRouter-Title (Preá). These values are sent only to OpenRouter’s HTTPS inference endpoints, never to compatible custom hosts. Recovery uses the existing configured deployment credential; no Redis or external accounting service is required.

The user value is not the account’s user ID. It is a pseudonym derived from the deployment’s master secret, so OpenRouter can still group one person’s requests without learning who that person is. The pseudonym is stable for the same account at the same provider and different at every provider, so no two providers can recognise the same person from their own records. A deployment with no master secret leaves the field out instead of sending an identifier; the operator sees one warning in the logs.

Stripe setup

The adapter pins Stripe API 2026-09-30.endive. Configure webhook destinations for your own account, using snapshot payloads on the same API version. Subscribe to all customer.subscription events, customer.deleted, checkout.session.completed, checkout.session.async_payment_succeeded, invoice.paid, invoice.payment_failed, charge.refunded, charge.dispute.created, and charge.dispute.closed. Credit Checkout restricts payment methods to cards. New subscriptions explicitly retain classic billing mode so API upgrades do not change invoice/proration behavior.

For local testing, forward Stripe test events to /api/v1/billing/webhook. Use the signing secret from stripe listen and test API credentials; keep them in an ignored environment file.

Support chat

CRISP_WEBSITE_ID on the API shows a Crisp support chat on the paywall.

Metering

Preá measures what a teammate consumes in three dimensions: task credits (model work), computer minutes (live desktops and sandboxes) and voice minutes (speech and calls). Every dimension is attributed to the workspace, the space and the teammate that spent it.

Billing stays off for metering until the operator sets an allowance:

Setting Dimension Unit
METER_TASK_CREDITS_LIMIT_USD task_credits USD of model work per month
METER_COMPUTER_MINUTES_LIMIT computer_minutes minutes of live desktop per month
METER_VOICE_MINUTES_LIMIT voice_minutes minutes of speech and calls per month

An unset setting leaves that dimension uncapped, which is what a deployment that meters its own internal customers without charging them wants. The allowance is per workspace and resets with the calendar month.

Before expensive work starts, the server checks what is left. A workspace that reached a limit pauses instead of failing: the API answers with a typed state that names the dimension, what was used and whether the deployment can take money for more, which the frontends render as a recharge prompt.

Brazilian payments

PIX charges, domestic credit cards and monthly plans run through Mercado Pago’s own API. Set MERCADO_PAGO_ACCESS_TOKEN and MERCADO_PAGO_WEBHOOK_SECRET to turn them on; both are required, and a half-configured deployment refuses to start rather than taking money it cannot reconcile. Amounts are always whole BRL centavos.

MERCADO_PAGO_PUBLIC_KEY is the account’s public key. It is public by design: the browser’s card form uses it to tokenize a card, and it grants no access to the account. With it set, the app’s content security policy allows the provider’s script, API and frame origins; without it, none of them are allowed.

Point the webhook at /api/v1/payments/webhook and subscribe it to payment, subscription and invoice notifications. The adapter checks the provider’s signature before it trusts a notification, and the delivery is processed once: a redelivery is acknowledged without moving money twice.

Cards are tokenized by the provider’s own front-end, so a card number never reaches the server. The server keeps the token, the card’s network and the last four digits. The network comes from the card form as well: the server never guesses it, and a charge without it is refused.

The payer’s CPF or CNPJ is optional. When one is given it is checked against its check digits and sealed with the workspace’s key before it is stored, and it is sent to the provider only on the payments that ask for it.

BRL prices

The credit wallet is kept in micro-USD, and a Brazilian workspace sees BRL prices. Set BRL_CENTS_PER_USD to the deployment’s own price of one US dollar of credit, in BRL centavos (550 is R$5,50 per US$1). The rate is a configured price, never a live quote, so nothing waits on a currency service, and the same pack always costs the same.

A workspace that runs out

Before a run starts, the server checks what the workspace has left. A workspace that reached a limit does not fail its work: the run waits with a typed state naming the dimension, what was used and what the workspace can do about it, a routine waits instead of being marked broken, and a voice call does not connect. The runs and routines go on by themselves once the credit arrives.

Plans

The product sells plans: free, pessoal, pro, equipe (priced per seat) and enterprise (the price is negotiated). A plan says what a workspace may do: how much model work and how much computer and voice time it has every month, how many cloud computers may run at once, how many agents and teammates it may have, and whether single sign-on and the audit log are on.

The plan is the source of the allowances the pre-flight check measures against, so a workspace that subscribes gets the plan’s numbers without any configuration. The operator’s own METER_* settings still win where they are set: they are the override for an installation that knows better than the plan.

Prices are the deployment’s own. Set PLAN_PRICES to a JSON object keyed by plan, for example {“pro”:{“monthlyCents”:14900,“annualCents”:149000}}, and the deployment charges those amounts. Unset, the release uses the placeholder prices that ship with it.

The installable edition

An installation that is licensed reads a license file, set with PREA_LICENSE_FILE. The license is signed by Orbin and verified offline: nothing is sent anywhere, and the installation keeps working without a network.

The license says who it is for (pessoal or empresa), how many seats and agents it covers, which features are on, and when it expires. An expired license keeps working for a grace period of 14 days; after that the installation is read-only: new work is refused with a typed error, and the data stays readable and exportable.

Install and inspect a license with the operator’s tool:

bun run --filter @orbin/api license install --file license.json --target /etc/prea/license.json
bun run --filter @orbin/api license inspect --file /etc/prea/license.json

Orbin issues the licenses. The issuing tool reads the private key from a path or from the environment, and the key never enters the repository:

bun run license:issue -- --licensee "Acme Ltda" --edition empresa --seats 10 --max-agents 50 \
  --days 365 --key /secure/orbin-license-key.pem --out license.json

The issuing tool refuses a private key that does not match the public key the release verifies with, so a license is never issued for a release that cannot check it.

A license on a development machine

Before Orbin provides the production key, a local install can use a development keypair of its own. The keypair is made on the spot, in the development directory, which is ignored by Git:

bun run license:dev

The command prints the environment to run with. A development install verifies with the key of its own keypair; a production install always verifies with the key compiled into the release, whatever the environment says.

Terms and plan changes

A plan is bought for a month or for a year. The annual plan is charged once for the year, by PIX or by card, and the invoice of the term is recorded like any other invoice.

Changing plan in the middle of a term does not charge the customer twice for the same days: the unused days of the plan it leaves are credited to the plan it moves to. A workspace that paid for a month and changes plan on the tenth day is credited with the twenty days it did not use, and the new plan is charged for the rest. The quote is visible before the payment: billing.planQuote says the price of the term, the credit and what is actually charged.