Skip to main content

APIs

ALPHA

Why the API entity?

In a typical API gateway setup, each HTTP endpoint is configured as an individual route. This works well for simple use cases, but as your platform grows, you end up with dozens or hundreds of routes that logically belong to the same service — they share the same backends, the same security policies, the same documentation. Managing them individually becomes tedious and error-prone.

The API entity solves this problem by providing a higher-level abstraction that groups everything related to a single API under one roof:

  • Multiple routes — define all the endpoints of your API (GET /users, POST /orders, etc.) in one place, with a shared domain and context path
  • Shared backends — define backend targets once and reuse them across routes, instead of duplicating target configurations
  • Plugin flows — create named plugin chains (authentication, rate limiting, transformations, etc.) and assign them to different routes as needed
  • Consumer plans — define who can access your API and how (API keys, JWT, mTLS, OAuth2), with pricing and quotas
  • Subscriptions — let developers or machines subscribe to your plans and receive auto-generated credentials
  • Lifecycle management — track your API from staging through published, deprecated, and removed
  • Testing mode — test draft changes safely before publishing, using a special header to access draft routes
  • Versioning and deployments — keep a history of published versions with full snapshots
  • Built-in documentation — attach pages, navigation, logos, and banners to power a developer portal

In short, the API entity turns Otoroshi from a route-level proxy into a full API Management platform: you design, secure, publish, version, and document your APIs in one place.

How it works at runtime

When Otoroshi starts or when an API is updated, each API is converted into a set of standard NgRoute objects that the proxy engine uses for request matching and forwarding. This conversion happens transparently:

  1. Each API route's frontend paths are prefixed with the API's domain and context_path (e.g., a route with path /users on an API with domain api.example.com and context path /v1 becomes api.example.com/v1/users)
  2. Backend references are resolved — either from the API's local backends or from global stored backends
  3. The plugin flow referenced by each route is attached as the route's plugin chain
  4. Published consumer plans inject the appropriate security plugins (API key validation, JWT verification, mTLS checks, etc.) into each route automatically
  5. If testing mode is enabled, additional draft routes are generated that require a special header to access

This means you get the organizational benefits of the API entity without any runtime overhead — under the hood, Otoroshi's engine processes the same optimized route structures it always has.

APIs vs. Routes — when to use which

Otoroshi offers two levels of abstraction for configuring your HTTP traffic, and they serve different purposes:

Routes: low-level HTTP routing

A route is a low-level, flexible building block. It gives you direct control over every aspect of HTTP proxying: exact domain matching, path patterns, headers, the full plugin chain, backend targets, TLS settings, etc. There are no constraints — you can wire anything to anything.

Routes are the right choice when:

  • You need maximum flexibility and fine-grained control over how a specific request is handled
  • You are proxying traffic that doesn't fit the "managed API" model (static assets, internal redirects, catch-all rules, WebSocket tunnels, TCP proxying, etc.)
  • You want to set up a quick one-off reverse proxy rule without the overhead of lifecycle management

APIs: structured API Management

An API is a higher-level, more opinionated entity built on top of routes. It adds structure and guardrails: a shared domain, a context path, a lifecycle (stagingpublisheddeprecatedremoved), consumer plans, subscriptions, versioning, deployments, and documentation.

This structure is deliberate — it trades some of the route-level flexibility for consistency and governance:

  • All routes in an API share the same domain and context path, so your URL scheme stays coherent
  • Security policies are defined as consumer plans rather than per-route plugin configurations, making it easier to reason about who has access to what
  • Lifecycle states prevent accidental exposure of work-in-progress endpoints
  • Subscriptions and credential generation provide a self-service workflow for API consumers
  • Deployment history gives you traceability of what was published and when

APIs are the right choice when:

  • You are exposing a service to external or internal consumers and want to manage access in a structured way
  • You need lifecycle management, versioning, and deployment tracking
  • You want to offer subscription plans with auto-generated credentials (API keys, JWT, OAuth2, mTLS)
  • You are building a developer portal

Under the hood, it's all routes

The key insight is that APIs are ultimately compiled down to routes at runtime. An API is a management-layer abstraction — it doesn't introduce a different proxy engine. When you create an API with 5 endpoints, Otoroshi generates 5 NgRoute objects behind the scenes, with the domain/context path prefixed, the backends resolved, and the consumer plan security plugins injected. So the choice between APIs and routes is really about how you want to organize and govern your configuration, not about runtime behavior.

UI page

You can find all APIs here

API entity properties

An API is the top-level entity that groups together routes, backends, flows, documentation, plans, subscriptions, and clients.

PropertyTypeDescription
idstringUnique identifier of the API
namestringDisplay name of the API
descriptionstringDescription of the API
domainstringThe domain on which the API is exposed (e.g., api.mydomain.com)
context_pathstringThe base path prefix for all routes in this API (e.g., /api/v1)
versionstringCurrent version of the API (e.g., 1.0.0)
versionsarray of stringList of all versions of this API
enabledbooleanWhether the API is active and serving traffic
statestringLifecycle state: staging, published, deprecated, or removed
blueprintstringAPI type: REST, GraphQL, gRPC, Http, or Websocket
debug_flowbooleanEnable debug flow logging for all routes
capturebooleanEnable request/response capture
export_reportingbooleanEnable detailed reporting export
groupsarray of stringService groups this API belongs to
tagsarray of stringFree tags for categorization
metadataobjectFree key/value metadata
routesarray of ApiRouteThe routes that compose this API
backendsarray of ApiBackendBackend definitions local to this API
flowsarray of ApiFlowsPlugin flow chains
clients_backend_configarray of ApiBackendClientBackend client configurations
documentationApiDocumentationAPI documentation and developer portal configuration
deploymentsarray of ApiDeploymentDeployment history
clientsarray of ApiClientRegistered API clients
testingApiTestingTesting/draft mode configuration

API lifecycle

An API goes through the following lifecycle states:

staging --> published --> deprecated --> removed
StateDescription
stagingThe API is being designed. Only draft/testing routes are served (if testing is enabled).
publishedThe API is live and serving traffic. Plans can accept subscriptions.
deprecatedThe API is still serving traffic but is marked for removal. No new subscriptions should be created.
removedThe API is deactivated. No traffic is served.

API blueprints

The blueprint field indicates the type of API being managed:

  • REST: Standard RESTful HTTP APIs
  • GraphQL: GraphQL APIs
  • gRPC: gRPC services
  • Http: Generic HTTP services
  • Websocket: WebSocket-based APIs

Routes

An API is composed of multiple routes. Each route describes an HTTP endpoint exposed by the API (e.g., GET /users, POST /orders).

A route is composed of a frontend (describing the entry point: domain, path, methods, headers), a reference to a backend, and a reference to a flow (plugin chain).

PropertyTypeDescription
idstringUnique identifier of the route
namestringDisplay name of the route (optional)
enabledbooleanWhether this route is active (default: true)
frontendobjectFrontend configuration (domains, paths, headers, methods, query, strip_path, exact)
backendstringReference to a backend ID (either a local API backend or a global stored backend)
flow_refstringReference to a flow ID from this API's flows list

The route's frontend paths are automatically prefixed with the API's domain and context_path. For example, if the API has domain: api.example.com and context_path: /v1, and a route has a frontend path /users, the effective matching path will be api.example.com/v1/users.

A route can be enabled or disabled independently of the API's activation.

Route JSON example

{
"id": "route_users_list",
"enabled": true,
"name": "List users",
"frontend": {
"domains": ["oto.tools/users"],
"strip_path": true,
"exact": false,
"headers": {},
"query": {},
"methods": ["GET"]
},
"backend": "api_backend_abc123",
"flow_ref": "default_plugin_chain"
}

Backends

A backend represents a list of target servers, along with its load balancing, health checks, and connection settings.

Backends can be defined locally within the API (in the backends array) or reference a global stored backend by its ID. This makes backends reusable across multiple APIs and routes.

PropertyTypeDescription
idstringUnique identifier of the backend
namestringDisplay name of the backend
backendobjectFull backend configuration (targets, root, rewrite, load balancing, health checks, TLS, etc.) following the NgBackend format
clientstringReference to a backend client configuration ID from clients_backend_config

Backend JSON example

{
"id": "api_backend_abc123",
"name": "Users service backend",
"backend": {
"targets": [
{
"id": "target_1",
"hostname": "users-service.internal",
"port": 8080,
"tls": false,
"weight": 1
}
],
"root": "/",
"rewrite": false,
"load_balancing": { "type": "RoundRobin" }
},
"client": "default_backend_client"
}

Backend clients

Backend clients define the HTTP client configuration used when connecting to backend targets. They are referenced by backends using the client field.

PropertyTypeDescription
idstringUnique identifier
namestringDisplay name
clientobjectClient configuration (retries, timeouts, circuit breaker, connection pool, etc.) following the NgClientConfig format

Backend client JSON example

{
"id": "default_backend_client",
"name": "Default client",
"client": {
"useCircuitBreaker": true,
"retries": 1,
"maxErrors": 20,
"retryInitialDelay": 50,
"backoffFactor": 2,
"callAndStreamTimeout": 120000,
"callTimeout": 30000,
"idleTimeout": 60000,
"globalTimeout": 30000,
"connectionTimeout": 10000
}
}

Flows

A flow is a named collection of plugins. Plugins are applied between the frontend and the backend (and vice versa) during request processing. Each route references a flow by ID.

PropertyTypeDescription
idstringUnique identifier of the flow
namestringDisplay name of the flow
pluginsobjectPlugin chain following the NgPlugins format

A default flow named default_plugin_chain is automatically created with the OverrideHost plugin. You can add multiple flows to apply different plugin chains to different routes within the same API.

You can check the list of available plugins here.

Flow JSON example

{
"id": "authenticated_flow",
"name": "Authenticated flow",
"plugins": {
"slots": [
{
"plugin": "cp:otoroshi.next.plugins.OverrideHost",
"enabled": true,
"include": [],
"exclude": [],
"config": {}
},
{
"plugin": "cp:otoroshi.next.plugins.ApikeyCalls",
"enabled": true,
"include": [],
"exclude": [],
"config": {}
}
]
}
}

Documentation

The API documentation subsystem powers developer portal experiences. It supports pages, navigation sidebars, search, banners, logos, and external content sources.

PropertyTypeDescription
enabledbooleanWhether the documentation is active
sourceobjectOptional remote source to fetch documentation content (URL, headers, timeout, follow_redirects)
homeobjectHome page resource
logoobjectLogo resource
referencesarrayList of external reference links (title, description, link, icon)
resourcesarrayList of documentation resources/pages
navigationarraySidebar navigation structure (categories and links)
redirectionsarrayURL redirections (from -> to)
footerobjectFooter resource (optional)
searchobjectSearch configuration (enabled: boolean)
bannerobjectBanner resource (optional)
plansarray of ApiDocumentationPlanSubscription plans with access modes and pricing
metadataobjectDocumentation metadata
tagsarray of stringDocumentation tags

Documentation resources

Each resource in the documentation can contain:

PropertyTypeDescription
patharray of stringPath segments for URL routing
titlestringPage title
descriptionstringPage description
content_typestringContent type (default: text/markdown)
text_contentstringInline text content
json_contentobjectInline JSON content
base64_contentstringBase64-encoded binary content
urlstringRemote URL to fetch content from
site_pagebooleanWhether this resource is a standalone site page
transformstringContent transformation to apply

Plans

Plans (also referred to as consumers in the UI) define security policies and pricing for API access. Each plan specifies an access mode that determines how clients authenticate when calling the API.

PropertyTypeDescription
idstringUnique identifier of the plan
namestringDisplay name of the plan
descriptionstringDescription of the plan
statusstringPlan status: staging, published, deprecated, or closed
access_mode_configuration_typestringAccess mode type (see below)
access_mode_configurationobjectAccess mode configuration (depends on type)
pricingApiPricingPricing configuration
tagsarray of stringTags
metadataobjectMetadata

Access modes

Otoroshi supports the following access modes for plans:

ModeDescriptionPlugins applied
keylessNo authentication required. Open access.None
apikeyRequires a valid API key. Configurable key patterns, restrictions, quotas, and rotation.ApikeyCalls
jwtRequires a valid JWT token with expected signature.JwtUserExtractor
mtlsRequires a valid TLS client certificate matching configured subject/issuer patterns.HasClientCertMatchingValidator
oauth2-localOAuth2 client credentials flow with local token generation. Clients obtain a JWT to call other routes.ApikeyCalls + client credentials endpoint
oauth2-remoteOAuth2 with remote token introspection via an external OIDC provider.OIDCJwtVerifier

Apikey access mode configuration

PropertyTypeDescription
clientIdPatternstringRegex pattern for generated client IDs
clientNamePatternstringRegex pattern for generated client names
descriptionstringDescription for generated API keys
authorizedEntitiesarrayEntities the API key is authorized on
enabledbooleanWhether generated keys are enabled
readOnlybooleanWhether generated keys are read-only
allowClientIdOnlybooleanAllow authentication with client ID only (no secret)
constrainedServicesOnlybooleanRestrict to constrained services only
restrictionsobjectAccess restrictions (allowed/forbidden paths and methods)
rotationobjectKey rotation configuration (enabled, rotationEvery, gracePeriod)
validUntilnumberExpiration timestamp (milliseconds)
tagsarray of stringTags for generated keys
metadataobjectMetadata for generated keys

Plan JSON example

{
"id": "plan_free_tier",
"name": "Free Tier",
"description": "Free access with rate limiting",
"status": "published",
"access_mode_configuration_type": "apikey",
"access_mode_configuration": {
"enabled": true,
"readOnly": false,
"allowClientIdOnly": false,
"constrainedServicesOnly": false,
"restrictions": {
"enabled": false
},
"rotation": {
"enabled": false,
"rotationEvery": 744,
"gracePeriod": 168
},
"tags": [],
"metadata": {}
},
"pricing": {
"id": "pricing_free",
"name": "Free",
"enabled": true,
"price": 0,
"currency": "EUR",
"params": {}
},
"tags": [],
"metadata": {}
}

Pricing

Plans can include pricing information for monetization purposes.

PropertyTypeDescription
idstringUnique identifier
namestringPricing plan name
enabledbooleanWhether pricing is active
pricenumberPrice amount
currencystringCurrency code (e.g., EUR, USD)
paramsobjectAdditional pricing parameters

Subscriptions

End-users (developers, portals, machines) can subscribe to published plans. A subscription contains information about the subscriber and references to the generated credentials (API key, certificate, JWT, etc.).

PropertyTypeDescription
idstringUnique identifier
namestringDisplay name
descriptionstringDescription
enabledbooleanWhether the subscription is active
api_refstringReference to the parent API
plan_refstringReference to the plan
owner_refstringReference to the subscribing client
subscription_kindstringKind of subscription: apikey, mtls, keyless, oauth2-local, oauth2-remote, jwt
token_refsarray of stringReferences to generated credentials (API key IDs, certificate IDs, etc.)
datesobjectLifecycle timestamps (see below)
tagsarray of stringTags
metadataobjectMetadata

Subscription dates

PropertyTypeDescription
created_atnumberTimestamp when subscription was created
processed_atnumberTimestamp when subscription was processed
started_atnumberTimestamp when subscription became active
paused_atnumberTimestamp when subscription was paused
ending_atnumberTimestamp when subscription will end
closed_atnumberTimestamp when subscription was closed

Subscription JSON example

{
"id": "subscription_abc123",
"name": "ACME Corp subscription",
"description": "Free tier subscription for ACME Corp",
"enabled": true,
"api_ref": "api_users_service",
"plan_ref": "plan_free_tier",
"owner_ref": "client_acme",
"subscription_kind": "apikey",
"token_refs": ["apikey_xyz789"],
"dates": {
"created_at": 1710000000000,
"processed_at": 1710000000000,
"started_at": 1710000000000,
"paused_at": 1710000000000,
"ending_at": 1710000000000,
"closed_at": 1710000000000
},
"tags": [],
"metadata": {}
}

API clients

Clients represent the consumers that subscribe to your API. They are lightweight entities used to identify subscribers.

PropertyTypeDescription
idstringUnique identifier
namestringClient name
descriptionstringDescription
tagsarray of stringTags
metadataobjectMetadata

Testing

The testing configuration allows you to test draft routes before publishing the API. When testing is enabled, Otoroshi generates special routes from the API's draft with a custom header requirement.

PropertyTypeDescription
enabledbooleanWhether testing mode is active
headerKeystringHeader name required to access draft routes (default: X-OTOROSHI-TESTING)
headerValuestringExpected header value (auto-generated UUID)

When testing is enabled:

  1. Draft routes are built from the latest saved draft of the API
  2. Each draft route requires the testing header (X-OTOROSHI-TESTING: <value>) to be accessible
  3. This allows you to test changes without affecting published routes

Deployments

Deployments track the history of API publications. Each time an API is published (its state transitions from staging to published), a deployment record is created.

PropertyTypeDescription
idstringUnique identifier
apiRefstringReference to the API
ownerstringWho performed the deployment
atnumberTimestamp of the deployment
apiDefinitionobjectSnapshot of the API definition at deployment time
versionstringVersion of the API at deployment time

OpenAPI import

Otoroshi can generate an API entity from an OpenAPI specification. When importing from an OpenAPI document:

  • The API name, description, and version are extracted from the info section
  • Server URLs are mapped to backend targets
  • Each path and method combination is converted to an API route
  • Both JSON and YAML formats are supported

Complete API JSON example

{
"id": "api_users_service",
"name": "Users Service",
"description": "API for managing users",
"domain": "api.example.com",
"context_path": "/v1",
"version": "1.0.0",
"versions": ["1.0.0"],
"enabled": true,
"state": "published",
"blueprint": "REST",
"debug_flow": false,
"capture": false,
"export_reporting": false,
"groups": ["default"],
"tags": ["users", "core"],
"metadata": {
"team": "platform"
},
"backends": [
{
"id": "users_backend",
"name": "Users backend",
"backend": {
"targets": [
{
"id": "target_1",
"hostname": "users-service.internal",
"port": 8080,
"tls": false,
"weight": 1
}
],
"root": "/",
"rewrite": false,
"load_balancing": { "type": "RoundRobin" }
},
"client": "default_backend_client"
}
],
"clients_backend_config": [
{
"id": "default_backend_client",
"name": "Default client config",
"client": {
"useCircuitBreaker": true,
"retries": 1,
"callTimeout": 30000,
"globalTimeout": 30000
}
}
],
"flows": [
{
"id": "default_plugin_chain",
"name": "Default flow",
"plugins": {
"slots": [
{
"plugin": "cp:otoroshi.next.plugins.OverrideHost",
"enabled": true,
"include": [],
"exclude": [],
"config": {}
}
]
}
}
],
"routes": [
{
"id": "route_list_users",
"enabled": true,
"name": "List users",
"frontend": {
"domains": ["oto.tools/users"],
"strip_path": true,
"exact": false,
"headers": {},
"query": {},
"methods": ["GET"]
},
"backend": "users_backend",
"flow_ref": "default_plugin_chain"
},
{
"id": "route_create_user",
"enabled": true,
"name": "Create user",
"frontend": {
"domains": ["oto.tools/users"],
"strip_path": true,
"exact": false,
"headers": {},
"query": {},
"methods": ["POST"]
},
"backend": "users_backend",
"flow_ref": "default_plugin_chain"
}
],
"documentation": {
"enabled": true,
"plans": [
{
"id": "plan_free",
"name": "Free Tier",
"description": "Free access with API key",
"status": "published",
"access_mode_configuration_type": "apikey",
"access_mode_configuration": {
"enabled": true,
"readOnly": false,
"allowClientIdOnly": false,
"tags": [],
"metadata": {}
},
"pricing": {
"id": "pricing_free",
"name": "Free",
"enabled": false,
"price": 0,
"currency": "EUR",
"params": {}
}
}
],
"home": {},
"logo": {},
"references": [],
"resources": [],
"navigation": [],
"redirections": [],
"search": { "enabled": true },
"metadata": {},
"tags": []
},
"clients": [
{
"id": "client_acme",
"name": "ACME Corp",
"description": "ACME Corp developer account",
"tags": [],
"metadata": {}
}
],
"testing": {
"enabled": false,
"headerKey": "X-OTOROSHI-TESTING",
"headerValue": "secret-test-value"
},
"deployments": []
}

Admin API

The API entity can be managed through the admin API:

GET    /api/apis                     # List all APIs
POST /api/apis # Create a new API
GET /api/apis/:id # Get an API by ID
PUT /api/apis/:id # Update an API
DELETE /api/apis/:id # Delete an API
PATCH /api/apis/:id # Partially update an API

Subscriptions are managed at:

GET    /api/api-subscriptions        # List all subscriptions
POST /api/api-subscriptions # Create a subscription
GET /api/api-subscriptions/:id # Get a subscription
PUT /api/api-subscriptions/:id # Update a subscription
DELETE /api/api-subscriptions/:id # Delete a subscription
  • Routes - The route entity that API routes are converted into at runtime
  • Backends - Global reusable backends that can be referenced by API backends
  • API Keys - Credentials generated when subscribing with apikey access mode
  • Certificates - Certificates used with mtls access mode
  • JWT Verifiers - Verifiers used with jwt and oauth2-remote access modes