Service Types¶
UnitySVC is a multi-protocol service marketplace. Sellers list services, and the platform handles discovery, authentication, routing, metering, and billing. This guide covers what types of services the platform supports and how they are delivered.
Service Categories¶
The platform supports services across multiple protocols and use cases:
| Category | Protocol | Gateway | Examples |
|---|---|---|---|
| AI / ML | HTTP | API Gateway | Chat APIs, embeddings, image generation, transcription, translation |
| Email delivery | SMTP | SMTP Gateway | Transactional email, bulk email, email verification |
| Content / media | HTTP or S3 | API Gateway or S3 Gateway | Stock media, datasets, video courses, software distribution |
| Compute environments | SSH / WireGuard | SSH Gateway | GPU instances, licensed software, dev environments |
| Database access | SSH tunnel | SSH Gateway | Managed Postgres, Redis, Elasticsearch |
| Monitoring | HTTP (platform-triggered) | API Gateway | Uptime checks, SSL monitoring, DNS monitoring |
| Scheduled tasks | HTTP (platform-triggered) | API Gateway | Cron-as-a-service, data collection, report generation |
| Webhook relay | HTTP | API Gateway | Webhook buffering, retry, fan-out, transformation |
| Notifications | HTTP | API Gateway | Push notifications, alerting |
AI and machine learning¶
Language models, embeddings, image generators, speech-to-text, and more. Customers access them through a unified HTTP API with the same API key. The platform supports intelligent routing across providers and automatic failover.
Service types: llm, embedding, rerank, image_generation, speech_to_text, text_to_speech, video_generation, text_to_image, vision_language_model, streaming_transcription, prerecorded_transcription, prerecorded_translation, text_to_3d
Email delivery¶
Sellers wrap SMTP providers (SendGrid, Mailgun, SES) as marketplace services. Customers point their app's SMTP settings at smtp.unitysvc.com and send. The seller's SMTP credentials are hidden — customers authenticate with their UnitySVC API key.
Service types: smtp_relay
Content and media¶
Sellers host files on S3, GCS, R2, or other storage. Customers browse and download through the S3 gateway (s3.unitysvc.com) using standard S3 tools (boto3, aws-cli, rclone). The platform meters downloads and hides the seller's storage credentials.
For video/audio streaming (HLS, DASH), the HTTP API gateway serves manifest files and media segments — standard HTTP, no special infrastructure needed.
Service types: content_delivery, video_streaming, audio_streaming, data_feed
Compute environments¶
Sellers offer on-demand computing environments (GPU instances, licensed software, analysis tools). Customers enroll, connect via SSH tunnel or WireGuard VPN, use the environment, and disconnect. Time-based billing charges only for active usage.
Service types: compute
Monitoring and scheduled tasks¶
The platform triggers actions on a schedule on behalf of customers. Sellers provide the checking/action logic; the platform handles scheduling, alerting, and billing. Uses the existing RecurrentRequest infrastructure.
Service types: uptime_monitoring, ssl_monitoring, dns_monitoring, cron, webhook_relay
Service Delivery Patterns¶
Orthogonal to the service category, each service uses one of these delivery patterns. The pattern determines who provides upstream credentials and whether enrollment is required.
| Pattern | Who pays provider? | Enrollment? | Customer action |
|---|---|---|---|
| Managed | Seller | No | None — use immediately |
| Managed + parameters | Seller | Yes | Enroll and configure |
| BYOK | Customer | No | Store API key as a secret |
| BYOE | Customer (self-hosted) | Yes | Enroll with endpoint URL |
| Recurrent | Any of the above | Yes | Enroll and configure schedule |
| Subscription | Seller | Yes | Enroll; platform charges daily/hourly via heartbeat |
Each delivery pattern corresponds to an upstream access channel in the offering's upstream_access_config — a complete way for the gateway to reach the upstream. The channel's channel_type (managed / byok / byoe) is derived from its credential and endpoint, not hand-labelled. A single offering can expose more than one channel — see Multi-channel services.
Managed services¶
A managed channel uses the seller's key (${ secrets.* }), so the seller provides upstream credentials. All customers share the same upstream endpoint. No enrollment required.
// specs/openai/gpt-4/offering.json — seller's upstream credentials
{
"upstream_access_config": {
"OpenAI API": {
"access_method": "http",
"base_url": "https://api.openai.com/v1",
"api_key": "${ secrets.OPENAI_API_KEY }"
}
}
}
// specs/openai/gpt-4/listing.json — customer-facing gateway path
{
"user_access_interfaces": {
"OpenAI API Access": {
"access_method": "http",
"base_url": "${API_GATEWAY_BASE_URL}/p/openai",
"routing_key": { "model": "gpt-4" }
}
}
}
Request flow: Customer → API key → Gateway → seller's upstream creds → Provider
BYOK (Bring Your Own Key)¶
A BYOK channel uses the customer's key (${ customer_secrets.* }), so the customer provides their own upstream API key. No enrollment required — the key lives in the customer's secret store. A BYOK channel references it with the customer_secrets namespace in upstream_access_config:
// specs/groq/llama-3.3-70b/offering.json
{
"upstream_access_config": {
"Groq API": {
"access_method": "http",
"base_url": "https://api.groq.com/openai/v1",
"api_key": "${ customer_secrets.GROQ_API_KEY }",
"routing_key": { "model": "llama-3.3-70b-versatile" }
}
}
}
The namespace of the reference — not its location — declares who owns the secret:
| Reference | Owner | Resolved from |
|---|---|---|
${ secrets.NAME } |
Seller | Seller's secret store |
${ customer_secrets.NAME } |
Customer (BYOK) | Customer's secret store |
A ${ customer_secrets.X } reference is also its own declaration — the platform auto-detects the customer-required secret by scanning for it; no separate user_parameters_schema entry is needed. See File Schemas for the full BYOK model.
BYOE (Bring Your Own Endpoint)¶
A BYOE channel uses the customer's key and a customer-templated base_url, so the customer provides the URL of their own service instance (e.g., self-hosted Ollama). Enrollment required. The key name is supplied per-enrollment via Jinja indirection inside the customer_secrets namespace:
// specs/ollama/byoe/offering.json — templates resolve from enrollment parameters
{
"upstream_access_config": {
"Ollama API": {
"access_method": "http",
"base_url": "{{ base_url }}",
"api_key": "${ customer_secrets.{{ api_key_secret }} }"
}
}
}
Two-phase resolution:
1. Jinja rendering (enrollment parameters): {{ base_url }} → http://my-server:11434, {{ api_key_secret }} → MY_KEY
2. Secret resolution: ${ customer_secrets.MY_KEY } → the actual key value from the customer's secret store
Multi-channel services¶
A single offering isn't limited to one channel. upstream_access_config can hold several upstream access channels at once — for example a managed channel and a byok channel reaching the same upstream — and the gateway picks one per request (by routing_key match, or forced with _channel=<name>). Each channel can carry its own price via a channel-keyed list_price, so the same model can be billed per-token on the seller's key and free on the customer's. See Channel-based Pricing for the upstream_access_config + list_price shape.
Multi-server channels (capacity & failover)¶
Planned — schema reserved, not yet served
The servers shape below is reserved and may be authored now, but the gateway does not yet
distribute or fail over across servers — today a channel uses its own top-level fields.
Tracked in unitysvc/unitysvc#1299.
Use multiple channels (above) when customers should choose between options (managed vs. BYOK).
Use multiple servers when you are scaling or hardening one option. A single channel can be
backed by several interchangeable servers — extra endpoints and/or keys for the same
channel_type — to spread load, survive an upstream outage or rate limit, and migrate endpoints
without downtime. Replace the channel's flat base_url / api_key with a servers list:
{
"upstream_access_config": {
"managed": {
"access_method": "http",
"routing_key": { "model": "deepseek-v4-pro" },
"servers": [
{ "base_url": "https://api.deepseek.com", "api_key": "${ secrets.KEY_A }", "weight": 3 },
{ "base_url": "https://api-backup.deepseek.com", "api_key": "${ secrets.KEY_B }", "weight": 1 }
]
}
}
}
How the gateway resolves a channel:
serverspresent — pick one server byweight, lift its fields onto the channel, then route as usual.- No
servers— use the channel's own top-level fields (the single-server case is unchanged).
Server entries are free-form — their fields are whatever that access_method needs (an http
server has base_url / api_key; an smtp server carries different keys), exactly the fields you'd
otherwise place at the channel level, plus an optional weight.
- Capacity —
weightspreads steady-state load proportionally (weight: 3takes ~3× the traffic ofweight: 1; omitted weights are equal). - Failover — if the chosen server returns a retriable upstream failure (
429,502/503/504, connection/timeout) before any response is sent, the gateway spills to another server in the same channel and retries, up to a bounded number of attempts. A client error (4xx) is never retried, and once a response has started streaming it cannot fail over. - Zero-downtime transition — add the new server, set the old one's
weight: 0to drain it (it stays a failover target but takes no new traffic), then remove it once traffic has moved.
All servers in a channel must be the same channel_type — failover never crosses provenance, so a
byok server is never swapped for a managed one (which would change whose key and bill applies).
Because the servers are interchangeable, the channel keeps one name, one channel_type, and
one price: multi-server is invisible to the customer and to
channel pricing.
Recurrent services¶
The platform triggers requests on a schedule. Recurrence is orthogonal to delivery pattern — any service type can be recurrent.
[service_options]
prompt_recurrence = true
recurrence_min_interval_seconds = 300 # minimum 5 minutes
recurrence_max_interval_seconds = 86400 # maximum 1 day
recurrence_allow_cron = true
| Option | Type | Default | Description |
|---|---|---|---|
prompt_recurrence |
bool | false |
Prompt for recurrence parameters during enrollment |
recurrence_min_interval_seconds |
int | 60 |
Minimum interval |
recurrence_max_interval_seconds |
int | 604800 |
Maximum interval (7 days) |
recurrence_allow_cron |
bool | true |
Allow cron expressions |
Subscription (time-based billing)¶
For services where customers pay for duration (compute, content libraries), the platform creates a daily/hourly heartbeat request via RecurrentRequest. Charges accrue while enrolled; cancellation stops charges. No new billing model — uses existing constant pricing + scheduled requests.
Per-enrollment code in URLs¶
When a URL needs a per-enrollment value (e.g. a unique topic code), reference the
intrinsic {{ enrollment.code }} directly — every enrollment has a unique code,
so no per-enrollment variable needs to be declared:
[user_access_interfaces.gateway]
access_method = "http"
base_url = "${API_GATEWAY_BASE_URL}/ntfy/{{ enrollment.code }}"
{{ enrollment.code }} renders to the enrollment's unique code (e.g. CEFF) at enrollment
time. The same enrollment is also reachable directly at /e/CEFF regardless of base_url.
See file-schemas.md for the full templating model, and service-templates.md for instantiating these patterns from a template.
Comparison Table¶
| Aspect | Managed | BYOK | BYOE | Recurrent | Subscription |
|---|---|---|---|---|---|
| api_key reference | ${ secrets.X } (seller) |
${ customer_secrets.X } (customer) |
${ customer_secrets.{{ param }} } |
Same as base | Same as base |
| upstream base_url | Static URL | Static URL | {{ base_url }} |
Same as base | Same as base |
| Enrollment? | No | No | Yes | Yes | Yes |
| Customer provides | Nothing | API key | Endpoint URL | Schedule | Nothing |
| Pricing | Per-request/token | Per-request/token | Per-request/token | Per-request/token | Time-based (daily/hourly) |
What Requires Enrollment?¶
| Condition | Why |
|---|---|
user_parameters_schema is non-empty |
Customer must provide configuration |
A user_access_interfaces / upstream_access_config template references {{ enrollment.* }} or {{ params.* }} |
Per-enrollment URL templating |
prompt_recurrence is true |
Per-enrollment schedule |
| Subscription pricing | Heartbeat linked to enrollment lifecycle |
Conditions that do not require enrollment:
| Condition | Why |
|---|---|
BYOK (${ customer_secrets.X }) |
Resolved from the customer's secret store at routing time |
Seller secrets (${ secrets.X }) |
Seller-level, resolved at routing time |
| Shared access interfaces | Same URL for all customers |
Service Groups¶
Services can be organized into service groups for unified routing. A customer sends a request to a group path (e.g., /g/llm/v1/chat/completions), and the platform routes to the best available service using weighted selection.
Groups can contain a mix of delivery patterns:
- Managed services — always available
- BYOK services — skipped if the customer hasn't stored the required secret
- BYOE services — skipped if the customer hasn't enrolled
This enables fallback patterns: route to the customer's BYOK key first, fall back to a seller-managed service if the key is missing.
Capability Pools¶
A capability pool (/p/<capability>) is a special kind of service group for a commodity capability — a model and contract fixed by the platform, offered by many providers at a single, uniform price. A customer sends a request to the pool path (e.g., /p/llama3-2-1b/v1/chat/completions) and the gateway load-balances across all verified providers of that capability. Because price and terms are identical across members, every provider is fungible, so the pool routes by performance (latency / quality / health) rather than cost.
You don't author a pool service by hand. Membership comes only from instantiating a pool-named service template — usvc_seller specs upload of a hand-written spec always produces a plain standalone service, never a pool member. Opting in is deliberately simple: instantiate the pool template (dashboard or usvc_seller params instantiate) and supply just your upstream URL (and a key secret, if your upstream needs one).
Pool membership is opt-in and non-exclusive — if you want to offer the same capability at your own price, publish it as a separate standalone service; joining a pool doesn't stop you.