# Integrate Zorveus into this project: Business

## What Zorveus does

Zorveus is an AI gateway that gives applications access to models from multiple providers. It supports OpenAI Chat Completions, OpenAI Responses, Anthropic Messages, and TypeSafe for typed System One decisions. An application can also use Zorveus for supported embeddings, image, video, speech generation, and audio transcription requests while retaining its existing client interfaces.

Zorveus also tracks AI usage and enforces allowances for individual product users. An inference key defines shared policy, such as allowed models, allowance amount, period, and overrun behavior. The application's stable user ID attributes each request, so users sharing a key have separate usage, credits, and applicable debt. Applications can map subscription plans to different keys, grant credits through trusted server operations, and receive usage and funding events through webhooks. The application's existing billing system still handles customer payments and subscriptions.

## Selected route and documentation

Use the Business route: this product funds customer inference. Do not implement user-funded OAuth unless the product owner explicitly adds that scope. Read https://docs.zorveus.com/llms.txt and its Markdown links first, especially /getting-started/integrate-your-product and /startups/configure-product-user-allowances. Create and manage apps, connections, API keys, caps, provider credentials, and webhook settings in the dashboard only. Service-key integration supports product-user operations under /product-users/by-external-id only. Status changes and credit revocation are dashboard-only. Local or OpenAPI routes do not establish supported public integration features.

## Set the integration scope

Inspect this project, then route every existing AI request path that Zorveus supports through Zorveus, unless the product owner specifies a narrower scope. Keep unsupported paths working and report them. Implement and test the change. Do not stop after a plan unless the repository requires approval before a substantial change. Do not ask questions that the code or official documentation can answer.

Use [Zorveus documentation](https://docs.zorveus.com) and the installed SDK as the source of truth for current routes, model availability, request fields, errors, and management methods. Do not invent an API when documentation is unavailable. Finish the work you can verify, then report the exact missing contract or product decision.

Always inspect inference, user attribution, errors, and tests. Apply the billing, purchased-credit, webhook, chat UI, and System One sections only when the host product uses those features. The concrete values below are examples, not a plan or price to impose on the product.

## Understand the product before editing

Find every AI request path, including API routes, workers, scheduled jobs, agents, embeddings, image and video work, speech, transcription, and typed AI decisions. Trace each path to its current client and provider. Identify the authentication boundary, stable internal user ID, existing provider abstraction, billing system, payment webhooks, secrets, retry rules, public error contracts, and tests.

Give a short inventory before choosing the integration. Preserve existing client behavior where possible. Do not assume that a project has subscriptions, payments, or every AI request type listed above. Name any path Zorveus cannot serve and keep its existing provider until the product owner decides what to do with it.

## Use the correct Zorveus model

- A Zorveus app represents this product. An inference key authenticates requests under an app connection. Each key defines shared policy, such as allowed models, an AI cap, its period, credit behavior, and overrun policy.
- Attribute a request to a product user with the product's stable internal user ID. Zorveus tracks that user's usage and credits inside the app. Do not create a second identity system or a key for every user.
- Users on the same key inherit its policy but have separate financial state. Moving a user between keys in the same app does not erase usage that falls in the new key's current allowance window.
- Zorveus calculates AI usage, allowance, credits, and applicable debt. Do not build a second spending ledger or calculate an allowance window in the product.
- The inference key authorizes inference. A service key authorizes trusted product-user and credit management. A webhook secret verifies outbound events. Keep all three server-side and use each only for its purpose.

The key's period can be daily, monthly, or lifetime. A product user's optional `allowance_starts_at` anchors recurring periods. For example, a monthly anchor of July 17 puts September 30 in the September 17 to October 17 window. The anchor does not make all usage since July 17 count in the current period. Zorveus owns that calculation.

## Route inference through the existing stack

Zorveus supports OpenAI Chat Completions, OpenAI Responses, Anthropic Messages, and TypeSafe System One. Preserve the API style each request path already uses. Reuse the corresponding SDK or client and configure it for Zorveus. OpenAI-compatible clients use `https://api.zorveus.com/v1`. For Anthropic Messages, use Zorveus's documented base URL and authentication configuration. Do not guess the Anthropic route or authentication headers. Preserve each API's message, tool, response, and streaming contracts instead of converting every path to Chat Completions.

If the project has a provider interface, add Zorveus to it. Otherwise configure the existing clients with the Zorveus key, base URL, selected model, extra headers, and optional extra body using the project's current conventions. Do not invent a provider hierarchy or create a separate client for each model vendor. Limit client changes to the paths being integrated.

Preserve the features each path already uses: streaming, tool calls, structured responses, images, audio, video, async execution, cancellation, tracing, and retries. Use an async client on an async path. For embeddings, keep the project's existing normalization and dimension handling unless Zorveus requires a different contract. A video path that uses the OpenAI API can use an OpenAI-compatible video client configured for Zorveus. A transcription model is a model choice, not a reason to name the whole client after Whisper.

Check Zorveus's documented task support and the chosen model's capabilities for each path. This includes text generation, embeddings, image understanding, image generation or editing, video, speech generation, audio transcription, and typed decisions where the product uses them. Support for one API style does not imply that every model accepts every task or request field. Preserve features that the selected model supports and report any capability gap before changing product behavior.

Direct the product owner to the [Zorveus model catalog](https://www.zorveus.com/models) to choose models for each AI task. First look for a direct Zorveus match for models the product already uses. If a choice is still needed, complete the client integration that does not depend on it and ask the owner for that model choice. Record chosen models in the host product's existing configuration. Keep model IDs as Zorveus accepts them. Examples include `gemini/<model>` and `zorveus/gpt-oss-20b`. Do not add wire-model translation or model-ID helper functions without a real compatibility need. Query `GET /v1/models` with each active inference key to verify that the chosen models are available to that key. Do not substitute an agent-preferred model for the product owner's choice.

For typed System One decisions, use the TypeSafe SDK with Zorveus at `https://api.zorveus.com/typesafe`. Consume the SDK's typed answers and preserve the application's decision result contract. Keep application decision interfaces independent of the model choice. For example, `typesafe/jev-latest` identifies a model; the application can name its abstraction `SystemOneClient` if that fits its existing architecture. Use this path when the product uses typed decisions rather than adding a new decision workflow solely for the integration.

## Attribute every user-scoped request

Get the external user ID from trusted authentication or worker state, never from an unchecked browser field. Prefer `X-Zorveus-External-User-ID` when the project needs only attribution. For optional dashboard details, use documented request metadata with `external_user_id` and `product_user` fields. Name, email, plan, and application metadata are enrichment, not identity. Do not send secrets or unnecessary personal data.

If the project creates a provider or client per user, set the external user ID when creating it. If it shares one client across users, attach the header to each request without mutating shared state. Carry the user ID through background jobs and media paths that bill a user. Reuse an authenticated user object when the request already has one; do not add a second database read solely for attribution.

This Python example shows one Chat Completions request. The caller supplies a client configured with the selected inference key and base URL. The caller also owns that client's lifecycle. For Responses, Anthropic Messages, or TypeSafe System One, retain that API's request shape and attach attribution through Zorveus's documented headers or metadata. Do not copy Chat Completions fields into a different API.

```python
from typing import Any

from openai import AsyncOpenAI

async def complete_for_user(
	client: AsyncOpenAI,
	model_id: str,
	message: str,
	user_id: str,
	product_user_details: dict[str, Any] | None = None,
):
	request_options: dict[str, Any] = {
		"extra_headers": {"X-Zorveus-External-User-ID": user_id}
	}
	if product_user_details is not None:
		request_options["extra_body"] = {
			"metadata": {
				"external_user_id": user_id,
				"product_user": product_user_details,
			}
		}

	return await client.chat.completions.create(
		model=model_id,
		messages=[{"role": "user", "content": message}],
		**request_options,
	)
```

Get `user_id` and any dashboard details from trusted server state. When the product needs enrichment, pass details such as `{"display_name": "Ada", "email": "ada@example.com", "metadata": {"plan": "pro"}}`. Get any plan value from trusted subscription state. When enrichment is unnecessary, omit `product_user_details`; the header alone attributes the request. Do not create a product user through the management API before every inference request merely to attach an ID. Use management calls when the product needs management data or an explicit update.

## Integrate billing when the project has it

If there is no subscription or payment system, use the configured default inference key. Do not invent plans, prices, checkout, or credit purchases. Leave a clear server-side key-selection point for later.

If plans already have different AI economics, map each trusted plan to an inference key in the same Zorveus app. Select the key from server-side subscription state, not a client-supplied plan name. Configure the key's models, cap, period, credit behavior, and overrun policy in Zorveus. Do not invent cap amounts or policy choices. Keep the user's external ID unchanged through upgrades and downgrades.

Here is an illustrative map. The amounts and policies are examples to replace with the host product's approved economics:

| Trusted plan | Server-side setting | Example policy on that Zorveus inference key |
| --- | --- | --- |
| Free | `ZORVEUS_FREE_KEY` | $2 per product user per month, selected low-cost models, `limit_output` |
| Pro | `ZORVEUS_PRO_KEY` | $25 per product user per month, broader models, `allow_overrun` |
| Business | `ZORVEUS_BUSINESS_KEY` | $100 per product user per month, broader models, `carry_overrun_forward` |

All three keys belong to the same Zorveus app. One key can serve many users. For example, a signed-in Pro user with internal ID `user-42` sends the Pro key and `X-Zorveus-External-User-ID: user-42`. A later downgrade changes the key selected for future requests, not the external user ID. Select the key at one trusted application boundary, such as an existing client or provider factory. Pass the selected configuration to the ordinary AI clients; do not scatter plan checks through chat, embedding, video, and audio calls.

Choose overrun behavior with the product owner. `limit_output` can restrict supported text output and reject variable-cost media when a hard cost limit is unavailable. `allow_overrun` lets a request finish and forgives excess allowance spend. `carry_overrun_forward` records excess as that user's debt against later compatible allowance or credits. Confirm the current behavior in Zorveus documentation before setting a policy.

### Set the AI allowance start when billing requires it

An inference key sets the cap amount and its daily, monthly, or lifetime period. A product user can also have one app-scoped `allowance_starts_at` timestamp, called **AI allowance start** in the product. The user does not have a separate period setting. If the product's AI allowance follows a subscription renewal date, set this timestamp from the trusted billing period start. Keep subscription prices, refunds, and proration in the product's billing system. Zorveus calculates the AI allowance window.

For each request, Zorveus uses the selected key's period and the user's start time:

| Key period | With `allowance_starts_at` | Without it |
| --- | --- | --- |
| Monthly | Anniversary windows from the original timestamp. A July 17 start gives September 17 to October 17 on September 30. | UTC calendar months, beginning at 00:00 on the first. |
| Daily | Successive 24-hour windows from the timestamp. | UTC calendar days, beginning at 00:00. |
| Lifetime | The timestamp does not change the cap window. | No reset. |

Window starts are included and ends are excluded. Monthly boundaries come from the original day, not the previous shortened month: January 31 leads to February 28 and then March 31, or February 29 in a leap year. The start time is separate from the Zorveus product user's `created_at`. Do not infer it from first inference or from today's date. An existing user first seen on August 10 may have a July 17 allowance start.

Set the start through an authenticated product-user create or update call. A server-controlled inference request can instead send `X-Zorveus-Allowance-Starts-At: 2026-07-17T00:00:00Z` with `X-Zorveus-External-User-ID: user-42`, or put the timestamp in `metadata.product_user.allowance_starts_at` beside the product-user details. For multipart requests, use the allowance header or the documented JSON-encoded metadata form field. If both the body and header supply a start, the body wins. The timestamp must use RFC 3339 with an explicit UTC offset; Zorveus stores its UTC equivalent. A start without a product user is invalid. An inference request cannot clear an existing start, and user OAuth requests cannot change it.

Omitting the field preserves the stored value. Repeating that value leaves the window unchanged. An authenticated management update can set a later start or pass explicit `null` to restore UTC calendar windows. A different start changes the window immediately; it does not delete earlier usage or reset credit grants. A start of now begins a new window, while a past start may include prior recorded usage in its current window. Future timestamps are rejected. An older timestamp than the stored start is rejected, so delayed requests cannot revert a plan change. Do not send a new current time on every inference request or use this field to schedule a future plan change.

Usage belongs to the product user inside one Zorveus app. A key change does not erase usage in the new key's current window. For example, $18 already spent in a monthly window leaves $32 of a $50 monthly cap after an upgrade, if the start stays the same. A switch to a daily key uses daily windows derived from that same start. Credits keep their own balances and expiry dates. Zorveus owns the calculation, including active reservations and any carry-forward debt. Do not build a second period calculator or spending ledger in the host product.

When a product-user read reports an allowance, Zorveus uses an active direct product-user cap if one exists. Otherwise it uses the user's last app connection, recorded even after a denied attributed request. The *next inference request* is always checked against the key used for that request. Read the returned `period_start` and `period_end` for the effective allowance window; one reset date on a shared key cannot describe every user's reset date. If the product shows calendar-month analytics, label those separately from current allowance-window usage.

Only send a start time on inference requests when the server controls those requests and the inference key. If an end user can call Zorveus directly with that key, a user-controlled start could create a fresh allowance window. In that setup, change the timestamp only through the authenticated product-user management API. A UTC timestamp also cannot express a renewal fixed to local wall-clock time across daylight-saving changes; resolve that requirement before using it as the billing anchor.

If the product grants or sells AI credits, use a trusted business event to authorize each grant. For purchases, verify the existing payment provider's webhook or equivalent server-side confirmation before calling Zorveus with the service key. A checkout redirect is not proof of payment. Use a stable payment or grant ID for idempotency, use decimal-safe amounts, and test duplicate payment delivery. Let Zorveus apply the key's overrun and debt rules. Do not implement those calculations in the product.

### Grant purchased credits after a verified payment

For a product using Stripe, follow this sequence. If the product uses another payment provider, use its equivalent verified server-side event.

Before enabling credit checkout, verify the units and currency of a Zorveus credit grant against the product's purchased-credit offer. Also confirm a documented idempotency key or a reliable way to reconcile an uncertain grant by payment reference. If neither exists, do not enable automated credit checkout. Route any payment already captured into manual reconciliation rather than retrying an uncertain grant.

1. Create a Checkout Session or PaymentIntent using the product's existing billing service. Associate it with a server-owned purchase record that stores the authenticated user ID, the authorized credit amount and currency, and the payment object ID. Do not take the amount or user ID from untrusted Stripe metadata alone.
2. Receive the Stripe payment webhook on the backend. Verify its signature against the raw request body using the existing Stripe integration. For Checkout, handle `checkout.session.completed` only when `payment_status` is `paid`; handle `checkout.session.async_payment_succeeded` for delayed methods. For a direct PaymentIntent flow, use `payment_intent.succeeded`. Choose the events that match the product's payment flow and grant once per payment, even if several success events arrive. A Checkout redirect or an unpaid Checkout Session does not authorize a grant. Follow [Stripe's Checkout fulfillment guide](https://docs.stripe.com/checkout/fulfillment.md?payment-ui=stripe-hosted) and [webhook signature guide](https://docs.stripe.com/webhooks/signature).
3. Look up the purchase record by its stable payment object ID. Check that the paid amount, currency, product, and state match. Claim the purchase once using a database uniqueness constraint or the product's existing idempotency mechanism.
4. Call the Zorveus product-user credit API with the service key, Zorveus app ID, stable external user ID, verified decimal-string amount, source, and reason. The Python SDK example uses `ZorveusServiceClient.product_users.grant_credit_by_external_id(app_id=..., external_user_id=..., amount=..., source=..., reason=...)`. Confirm the current SDK signature and credit units before coding. If the webhook handler is async, use the async SDK or a durable worker rather than calling a blocking client on the event loop.
5. Record the returned Zorveus credit-grant ID against the purchase. If the Zorveus call times out after sending, treat the result as unknown. Reconcile with Zorveus before another grant. Use a documented Zorveus idempotency key if available; a local `processed` flag alone cannot prevent a double grant after an uncertain network result.
6. Acknowledge a duplicate Stripe webhook without granting again. Test duplicate delivery and a crash between the Zorveus response and the local update.

Promotional credits use the same service-key operation, but the grant authority is a trusted promotion, referral, or admin event. Stripe's webhook is **not** the Zorveus `wallet.payment_credited` event. The latter reports funding of the organization's Zorveus wallet; it does not prove that one of the product's customers purchased AI credits.

For a Python product, the credit request can look like this after the payment checks and one-time claim. Pass in a service client owned and closed by the caller. This call does not make the operation idempotent by itself:

```python
from zorveus import ZorveusServiceClient

def grant_purchased_credit(service: ZorveusServiceClient, purchase, app_id: str):
	return service.product_users.grant_credit_by_external_id(
		app_id=app_id,
		external_user_id=str(purchase.user_id),
		amount=purchase.credit_amount_decimal_string,
		source="service_key",
		reason=f"purchase:{purchase.payment_id}",
	)
```

Verify the import and method signature against the installed Zorveus SDK. The purchase record, not a client request body, must supply the amount. Store the returned grant ID and never treat the `reason` string as an idempotency key unless Zorveus documents that guarantee.

If billing behavior needs an amount, renewal rule, or overrun policy that the project does not define, complete the unblocked integration and ask for that one product decision before enabling the billing branch.

If the product refunds a credit purchase, inspect its refund policy and Zorveus's current credit-reversal support before changing a product user's credits. Already-spent credits may not be reversible. Do not silently erase a grant, issue a negative grant, or rebuild Zorveus's balance locally.

## Handle limits and webhooks as product states

Use structured Zorveus error codes or SDK error types. The Python SDK example includes `parse_zorveus_gateway_error`; an example allowance code is `zorveus_product_user_allowance_insufficient`. Confirm current codes before mapping them to public application errors. Never classify a failure by matching English text or treat every `403` as an exhausted product user. Do not retry a confirmed allowance, model-policy, or funding rejection as a transient provider error. Preserve bounded retries for genuine transient failures.

If the product needs an allowance notice, expose an observed user state rather than a local balance calculation. A confirmed product-user allowance error can mark the user exhausted. Before rejecting a later message, recheck Zorveus so a new period or credits can restore access. A failed check must not silently become permission to spend. Keep one allowance service that both inference error handlers and webhooks can call; do not put database writes in a context object or every individual workflow.

For the Python SDK, retrieve the profile with `service.product_users.get_by_external_id(app_id=..., external_user_id=...)`. For an active product user with an enforced allowance, check `profile.get_total_available_to_spend_decimal() > 0`, or use `profile.has_available_allowance()`. The helper reads `allowance.available_allowance`, falls back to `credits.available_credits` when no allowance value exists, and returns zero when both are absent. Zorveus calculates the reported allowance from remaining base allowance and available credits. Use the returned amount rather than recalculating it in the application. A positive amount permits an attempt; Zorveus still checks whether the actual request can be funded. Interpret an absent allowance according to the connection's cap and credit policy, because the helper's zero fallback does not establish that an uncapped connection is exhausted. When a plan selects a different key, account for the reported allowance's connection before using it to reject a request on the new key.

When using Zorveus outbound webhooks, verify `X-Zorveus-Signature` against the exact raw body. The signed bytes are the header timestamp, a period, and the raw request body. Check the timestamp and deduplicate by event ID. Read the product user from `data.external_user_id`. `product_user.cap_reached` concerns an attributed product user, even when `cap_target_type: "app_connection"` names the inference key whose cap supplied the policy. `app_connection.cap_reached` concerns a request without a product user. On `product_user.credits_granted`, check the current allowance before clearing an exhausted state; a grant may be too small. If one webhook destination receives events for several apps, scope product-user events by `data.app_id` before updating local users.

Subscribe only to events the product handles. This is the Zorveus outbound event catalog:

| Event | Meaning | Typical application action |
| --- | --- | --- |
| `product_user.cap_reached` | An attributed product user hit a product-user or app-connection cap. `data.external_user_id` identifies the user. | Mark that user's observed allowance state and show an appropriate notice. Use `data.app_id` to scope the event. |
| `product_user.credits_granted` | Zorveus granted a product user credit. | Recheck that user's current allowance before clearing a limit notice. |
| `app_connection.cap_reached` | A request without a product user hit an app-connection cap. | Alert operators or pause the affected anonymous workload. Do not flag every user. |
| `wallet.low_balance` | The organization's Zorveus wallet fell below its alert threshold. | Notify operators or trigger the existing funding workflow. |
| `wallet.payment_credited` | A Zorveus wallet top-up was credited. | Reconcile organization funding. Do not grant an end user purchased credits from this event. |
| `wallet.payment_failed` | A Zorveus wallet top-up failed. | Alert operators and reconcile organization funding. |
| `provider_credential.quota_exhausted` | An upstream provider credential exhausted its quota. | Alert operators and apply the product's existing provider-failure policy. |

The webhook envelope has `id`, `event`, `org_id`, `timestamp`, and `data`. For example, `product_user.cap_reached` includes `data.app_id`, `data.external_user_id`, `data.app_connection_id`, `data.cap_target_type`, `data.cap_amount`, and spend amounts. Do not parse `product_end_user_id` or an app-connection ID as the host product's user ID.

Use the event data, not a name guessed from the HTTP route. A typical attributed cap event has this shape:

```json
{
	"id": "outbox_104",
	"event": "product_user.cap_reached",
	"org_id": "org_123",
	"timestamp": "2026-09-29T10:00:00Z",
	"data": {
		"app_id": "app_123",
		"external_user_id": "user-42",
		"app_connection_id": "aconn_123",
		"cap_target_type": "app_connection",
		"cap_amount": "25.00",
		"spent_this_period": "24.00",
		"requested_amount": "2.00",
		"currency": "USD"
	}
}
```

The `cap_target_type` value tells you which policy supplied the cap. It does not turn an attributed `product_user.cap_reached` event into an anonymous `app_connection.cap_reached` event. A credit event instead carries `data.external_user_id`, `data.app_id`, `data.credit_grant_id`, `data.granted_amount`, `data.currency`, and optional `data.expires_at`.

Parse `X-Zorveus-Signature` as `t=<unix-seconds>,v1=<hex-digest>`. Compute `HMAC-SHA256(webhook_secret, ascii(t) + b"." + raw_body)` and compare digests in constant time. Reject a missing or stale timestamp; use a five-minute tolerance unless current docs specify another value. Deduplicate on envelope `id` because delivery is at least once. Return `2xx` after durable acceptance. Zorveus retries a timeout, `429`, or `5xx`, but not ordinary `4xx`. Keep the receiver short and move slower work to the host product's durable task system.

For a chat product, follow its existing message-admission policy. The product may reject an exhausted user's new message before persistence or store it with an explicit failed state. In either case, return a stable application error through HTTP or WebSocket and do not leave the message appearing to process forever. If a message was already accepted when inference failed, preserve it and send a safe terminal failure. Let the frontend show a conversation-level allowance notice from the user's observed state. Do not present an exact reset date unless the product API exposes one.

## Set up Zorveus in the dashboard

If Zorveus is not configured, give the operator these steps in the handoff. Perform non-secret configuration only when the task and access permit it. Dashboard labels can change; verify them in the current UI or docs instead of claiming an unverified menu path.

1. Open the [Zorveus dashboard](https://app.zorveus.com). Create or select the business organization or workspace under [Organizations](https://app.zorveus.com/orgs).
2. Create or select the Zorveus application that represents the host product. Record its app ID as `ZORVEUS_APP_ID`. Put all plan-specific keys for that product under this same app.
3. Open **API Keys**, select the app, and create an **Inference Key** for the default policy. If the product has plan-specific AI terms, create one inference key for each distinct policy, such as Free, Pro, and Business. Record each key in server-side settings, such as `ZORVEUS_FREE_KEY`, `ZORVEUS_PRO_KEY`, and `ZORVEUS_BUSINESS_KEY`.
4. Ask the product owner to choose task-appropriate models from the [Zorveus model catalog](https://www.zorveus.com/models). On each inference key or its app-connection policy, configure those allowed models, the per-product-user AI cap, daily, monthly, or lifetime period, credit behavior, funding behavior, and overrun policy. Get the amounts and model access from the product owner. The key sets shared policy; it does not create a separate key or balance for each user.
5. If the product manages users or grants credits, create the organization's **Service Key** and store it as `ZORVEUS_SERVICE_KEY` on the backend. Verify its product-user and credit permissions. Do not use the inference key for management calls.
6. If the product receives Zorveus events, open the organization's webhook settings. Set a public HTTPS destination owned by the product, subscribe to the needed event types from the table above, and enable delivery. Store the webhook secret as `ZORVEUS_WEBHOOK_SECRET` when Zorveus first displays it. Later reads return only a preview. Zorveus currently supports one destination per organization, so route subscribed events by `event` and `data.app_id` inside that receiver.
7. If the organization's wallet or upstream provider credentials need setup for inference, complete those Zorveus dashboard steps using the product's funding policy. Do not confuse wallet funding with a customer's subscription or purchased AI credits.
8. With each active inference key, call `GET https://api.zorveus.com/v1/models` and confirm the owner's chosen model IDs are available. Run a low-cost attributed request and inspect the product user and usage in the dashboard. If billing exists, also test the plan key, AI allowance start, and a small credit grant in a safe environment.

Give the operator a settings checklist without exposing values:

| Server-side setting | Needed for |
| --- | --- |
| `ZORVEUS_INFERENCE_KEY` | One default inference policy when the app does not route plans to different keys. |
| `ZORVEUS_FREE_KEY`, `ZORVEUS_PRO_KEY`, `ZORVEUS_BUSINESS_KEY` | Example plan-specific inference policies when billing terms require them. Use the host app's actual plan names. |
| `ZORVEUS_APP_ID` | Product-user management, credit grants, and webhook app scoping. |
| `ZORVEUS_SERVICE_KEY` | Trusted product-user reads, updates, and credit grants. |
| `ZORVEUS_WEBHOOK_SECRET` | Verification of Zorveus outbound events. |

Keep the default inference base URL in provider configuration. Add a setting for it only if the host app needs to change the endpoint between environments.

## Keep the integration narrow

- Keep secrets in the project's server-side settings or secret manager. Never log or expose keys, webhook signatures, full provider errors, or sensitive metadata.
- Follow the project's configuration conventions. Typical names are `ZORVEUS_INFERENCE_KEY`, `ZORVEUS_SERVICE_KEY`, `ZORVEUS_APP_ID`, and `ZORVEUS_WEBHOOK_SECRET`. Add plan-specific key names only when billing uses them.
- Preserve explicit provider injection. Configuring a Zorveus key must not make unrelated clients invalid or force every call through Zorveus.
- Do not add duplicate clients, global user-ID lookups, compatibility shims for code just introduced, or special cases in unrelated provider implementations.
- Follow the repository's router, service, persistence, worker, and approval rules. Reuse existing payment and task infrastructure.
- Do not implement a Zorveus feature merely because it exists. Integrate the paths and billing behavior this product actually uses.

## Verify and hand off

Test the AI request types in scope, chosen model IDs, provider injection, attribution headers and metadata, async calls, error translation, and preserved streaming or tool behavior. Test the actual API styles used by the product: Chat Completions, Responses, Anthropic Messages, TypeSafe System One, or a combination. For System One, also test typed answers against the application's decision contract. Where applicable, test trusted plan-to-key selection, allowance recovery, webhook signature and replay protection, duplicate events, insufficient credit grants, and idempotent purchased credits. For AI allowance starts, test an unset start, a repeated start, an older rejected start, a key change within one app, a month-end boundary, and a product-user read after a denied attributed request. Use valid fixtures for the installed SDK version.

Run the repository formatter and affected tests. If safe test credentials exist, make a low-cost live request with a model returned by the active key. Never make live credentials a requirement for unit tests or print them in a handoff.

Report which AI paths now use Zorveus, which paths intentionally do not, how user attribution works, which environment variables and dashboard settings are needed, how any billing plan maps to keys, tests run, and remaining unverified behavior. If configuration is missing, give exact steps in the [Zorveus dashboard](https://app.zorveus.com) to create or select an organization, app, inference keys, service key, and webhook subscription as needed. Name the environment variables without showing their values. Do not ask the user to paste secrets into chat. Do not claim completion for paths that were not inspected or tested.
