From fef554b30fc34f4ed01dbe491d987bd003eb8160 Mon Sep 17 00:00:00 2001 From: Thushani Jayasekera Date: Wed, 29 Jul 2026 16:41:49 +0530 Subject: [PATCH 1/7] Update API documentation for certificate management and introduce new platform API endpoints - Removed the `developer` role requirement for uploading TLS certificates in the certificate management documentation. - Added new documentation files for API key management, application management, authentication, gateway policies, gateway tokens, and LLM provider deployments, detailing their respective operations, parameters, and responses. - Enhanced overall clarity and structure of the API documentation to improve usability for developers. --- .../gateway/certificate-management.md | 2 +- docs/rest-apis/platform-api/README.md | 213 + docs/rest-apis/platform-api/api-keys.md | 113 + docs/rest-apis/platform-api/applications.md | 1548 +++++ docs/rest-apis/platform-api/authentication.md | 358 ++ .../platform-api/gateway-policies.md | 431 ++ docs/rest-apis/platform-api/gateway-tokens.md | 358 ++ docs/rest-apis/platform-api/gateways.md | 809 +++ .../platform-api/llm-provider-deployments.md | 767 +++ .../platform-api/llm-provider-templates.md | 1502 +++++ docs/rest-apis/platform-api/llm-providers.md | 2012 ++++++ docs/rest-apis/platform-api/llm-proxies.md | 1315 ++++ .../platform-api/llm-proxy-deployments.md | 767 +++ docs/rest-apis/platform-api/mcp-proxies.md | 962 +++ .../platform-api/mcp-proxy-deployments.md | 723 +++ docs/rest-apis/platform-api/organizations.md | 372 ++ docs/rest-apis/platform-api/projects.md | 620 ++ .../platform-api/rest-api-deployments.md | 789 +++ docs/rest-apis/platform-api/rest-apis.md | 1904 ++++++ docs/rest-apis/platform-api/schemas.md | 5413 +++++++++++++++++ docs/rest-apis/platform-api/secrets.md | 639 ++ .../platform-api/subscriptionplans.md | 610 ++ docs/rest-apis/platform-api/subscriptions.md | 676 ++ platform-api/Makefile | 18 +- platform-api/resources/openapi.yaml | 8 + .../widdershins_templates/authentication.def | 32 + .../widdershins_templates/code_shell.dot | 18 +- 27 files changed, 22975 insertions(+), 4 deletions(-) create mode 100644 docs/rest-apis/platform-api/README.md create mode 100644 docs/rest-apis/platform-api/api-keys.md create mode 100644 docs/rest-apis/platform-api/applications.md create mode 100644 docs/rest-apis/platform-api/authentication.md create mode 100644 docs/rest-apis/platform-api/gateway-policies.md create mode 100644 docs/rest-apis/platform-api/gateway-tokens.md create mode 100644 docs/rest-apis/platform-api/gateways.md create mode 100644 docs/rest-apis/platform-api/llm-provider-deployments.md create mode 100644 docs/rest-apis/platform-api/llm-provider-templates.md create mode 100644 docs/rest-apis/platform-api/llm-providers.md create mode 100644 docs/rest-apis/platform-api/llm-proxies.md create mode 100644 docs/rest-apis/platform-api/llm-proxy-deployments.md create mode 100644 docs/rest-apis/platform-api/mcp-proxies.md create mode 100644 docs/rest-apis/platform-api/mcp-proxy-deployments.md create mode 100644 docs/rest-apis/platform-api/organizations.md create mode 100644 docs/rest-apis/platform-api/projects.md create mode 100644 docs/rest-apis/platform-api/rest-api-deployments.md create mode 100644 docs/rest-apis/platform-api/rest-apis.md create mode 100644 docs/rest-apis/platform-api/schemas.md create mode 100644 docs/rest-apis/platform-api/secrets.md create mode 100644 docs/rest-apis/platform-api/subscriptionplans.md create mode 100644 docs/rest-apis/platform-api/subscriptions.md diff --git a/docs/rest-apis/gateway/certificate-management.md b/docs/rest-apis/gateway/certificate-management.md index e001645928..ebc46de136 100644 --- a/docs/rest-apis/gateway/certificate-management.md +++ b/docs/rest-apis/gateway/certificate-management.md @@ -95,7 +95,7 @@ Upload a new TLS certificate (PEM format) to the Gateway. The certificate is loa diff --git a/docs/rest-apis/platform-api/README.md b/docs/rest-apis/platform-api/README.md new file mode 100644 index 0000000000..6757d57ad1 --- /dev/null +++ b/docs/rest-apis/platform-api/README.md @@ -0,0 +1,213 @@ + +

WSO2 API Platform - Platform API v0.9.0

+ +This document specifies a **RESTful API** for WSO2 **API Platform** - **Platform API**. + +## Authentication + +Most API endpoints require a Bearer JWT token in the `Authorization` header. Tokens are +validated against a configured Identity Provider (IDP) using JWKS-based signature +verification. + +**Required claim**: `organization` — UUID of the caller's organization. All operations are +automatically scoped to this organization. + +**Supported signing algorithms**: RS256, RS384, RS512 (RSA) and ES256, ES384, ES512 (ECDSA). +Works with any standards-compliant IDP (Keycloak, Azure AD, Okta, Auth0, WSO2 IS, etc.). + +## Scope-Based Access Control + +Authorization is enforced via OAuth2 scopes carried in the JWT. Both read (GET) and write +operations require specific scopes declared in the `security` field of each operation. + +Base URLs: +* https://localhost:9243/api/v0.9 +* https://api.platform.com/api/v0.9 + +License: Apache 2.0 + +## Table of Contents + +### [Authentication](authentication.md) + +- [Overview](authentication.md#overview) +- [Authentication Modes](authentication.md#authentication-modes) +- [Required Claims](authentication.md#required-claims) +- [Authorization](authentication.md#authorization) +- [Error Responses](authentication.md#error-responses) +- [Troubleshooting](authentication.md#troubleshooting) +- [Scope Reference](authentication.md#scope-reference) + +### [Organizations](organizations.md) + +- [Register a new organization](organizations.md#register-a-new-organization) +- [List organizations](organizations.md#list-organizations) +- [Get organization by ID](organizations.md#get-organization-by-id) +- [Check if organization exists](organizations.md#check-if-organization-exists) + +### [Projects](projects.md) + +- [Create a new project](projects.md#create-a-new-project) +- [Get all projects for current user's organization](projects.md#get-all-projects-for-current-users-organization) +- [Get project by UUID](projects.md#get-project-by-uuid) +- [Update project](projects.md#update-project) +- [Delete project](projects.md#delete-project) + +### [Applications](applications.md) + +- [Create a new application](applications.md#create-a-new-application) +- [Get applications for current user's organization](applications.md#get-applications-for-current-users-organization) +- [Get application by handle](applications.md#get-application-by-handle) +- [Update application](applications.md#update-application) +- [Delete application](applications.md#delete-application) +- [List application API key mappings](applications.md#list-application-api-key-mappings) +- [Add application API key mappings](applications.md#add-application-api-key-mappings) +- [Remove application API key mapping](applications.md#remove-application-api-key-mapping) +- [List application associations](applications.md#list-application-associations) +- [Add application associations](applications.md#add-application-associations) +- [Remove application association](applications.md#remove-application-association) +- [List application API key mappings for an association](applications.md#list-application-api-key-mappings-for-an-association) + +### [REST APIs](rest-apis.md) + +- [Get all REST APIs for an organization](rest-apis.md#get-all-rest-apis-for-an-organization) +- [Create a new REST API](rest-apis.md#create-a-new-rest-api) +- [Get REST API by UUID](rest-apis.md#get-rest-api-by-uuid) +- [Update REST API](rest-apis.md#update-rest-api) +- [Delete REST API](rest-apis.md#delete-rest-api) +- [Get gateways for REST API](rest-apis.md#get-gateways-for-rest-api) +- [Add gateways for REST API](rest-apis.md#add-gateways-for-rest-api) +- [Create API key](rest-apis.md#create-api-key) +- [Update API key](rest-apis.md#update-api-key) +- [Revoke API key](rest-apis.md#revoke-api-key) + +### [REST API Deployments](rest-api-deployments.md) + +- [Create and deploy a new deployment](rest-api-deployments.md#create-and-deploy-a-new-deployment) +- [Get deployments for a REST API](rest-api-deployments.md#get-deployments-for-a-rest-api) +- [Get deployment by ID](rest-api-deployments.md#get-deployment-by-id) +- [Delete deployment](rest-api-deployments.md#delete-deployment) +- [Undeploy deployment from gateway](rest-api-deployments.md#undeploy-deployment-from-gateway) +- [Restore a previous deployment](rest-api-deployments.md#restore-a-previous-deployment) + +### [Gateways](gateways.md) + +- [Register a new gateway](gateways.md#register-a-new-gateway) +- [List all gateways](gateways.md#list-all-gateways) +- [Get gateway by UUID](gateways.md#get-gateway-by-uuid) +- [Update gateway](gateways.md#update-gateway) +- [Delete gateway](gateways.md#delete-gateway) +- [Get gateway policy manifest](gateways.md#get-gateway-policy-manifest) + +### [Gateway Tokens](gateway-tokens.md) + +- [List active gateway tokens](gateway-tokens.md#list-active-gateway-tokens) +- [Rotate gateway token](gateway-tokens.md#rotate-gateway-token) +- [Revoke gateway token](gateway-tokens.md#revoke-gateway-token) + +### [Gateway Policies](gateway-policies.md) + +- [Get synced custom policies for the current organization](gateway-policies.md#get-synced-custom-policies-for-the-current-organization) +- [Sync a custom policy from the gateway manifest](gateway-policies.md#sync-a-custom-policy-from-the-gateway-manifest) +- [Get a specific custom policy version](gateway-policies.md#get-a-specific-custom-policy-version) +- [Delete a specific custom policy version](gateway-policies.md#delete-a-specific-custom-policy-version) + +### [LLM Providers](llm-providers.md) + +- [Create a new LLM provider](llm-providers.md#create-a-new-llm-provider) +- [List all LLM providers](llm-providers.md#list-all-llm-providers) +- [Get LLM provider by identifier](llm-providers.md#get-llm-provider-by-identifier) +- [Update an existing LLM provider](llm-providers.md#update-an-existing-llm-provider) +- [Delete an LLM provider](llm-providers.md#delete-an-llm-provider) +- [List LLM proxies by provider](llm-providers.md#list-llm-proxies-by-provider) +- [Create a new API key for an LLM provider](llm-providers.md#create-a-new-api-key-for-an-llm-provider) +- [List API keys for an LLM provider](llm-providers.md#list-api-keys-for-an-llm-provider) +- [Delete an API key for an LLM provider](llm-providers.md#delete-an-api-key-for-an-llm-provider) + +### [LLM Proxies](llm-proxies.md) + +- [Create a new LLM proxy](llm-proxies.md#create-a-new-llm-proxy) +- [List all LLM proxies](llm-proxies.md#list-all-llm-proxies) +- [Get LLM proxy by unique identifier](llm-proxies.md#get-llm-proxy-by-unique-identifier) +- [Update an existing LLM proxy](llm-proxies.md#update-an-existing-llm-proxy) +- [Delete an LLM proxy](llm-proxies.md#delete-an-llm-proxy) +- [Create a new API key for an LLM proxy](llm-proxies.md#create-a-new-api-key-for-an-llm-proxy) +- [List API keys for an LLM proxy](llm-proxies.md#list-api-keys-for-an-llm-proxy) +- [Delete an API key for an LLM proxy](llm-proxies.md#delete-an-api-key-for-an-llm-proxy) + +### [LLM Provider Templates](llm-provider-templates.md) + +- [Create a new LLM provider template family](llm-provider-templates.md#create-a-new-llm-provider-template-family) +- [List templates, list a family's versions, or get a single version](llm-provider-templates.md#list-templates-list-a-familys-versions-or-get-a-single-version) +- [Create a new version by copying an existing one](llm-provider-templates.md#create-a-new-version-by-copying-an-existing-one) +- [Get LLM provider template by id](llm-provider-templates.md#get-llm-provider-template-by-id) +- [Update an existing LLM provider template](llm-provider-templates.md#update-an-existing-llm-provider-template) +- [Enable or disable a template version](llm-provider-templates.md#enable-or-disable-a-template-version) +- [Delete a template version](llm-provider-templates.md#delete-a-template-version) + +### [LLM Provider Deployments](llm-provider-deployments.md) + +- [Create and deploy a new LLM provider deployment](llm-provider-deployments.md#create-and-deploy-a-new-llm-provider-deployment) +- [Get deployments for an LLM provider](llm-provider-deployments.md#get-deployments-for-an-llm-provider) +- [Get LLM provider deployment by ID](llm-provider-deployments.md#get-llm-provider-deployment-by-id) +- [Delete LLM provider deployment](llm-provider-deployments.md#delete-llm-provider-deployment) +- [Undeploy LLM provider deployment from gateway](llm-provider-deployments.md#undeploy-llm-provider-deployment-from-gateway) +- [Restore a previous LLM provider deployment](llm-provider-deployments.md#restore-a-previous-llm-provider-deployment) + +### [LLM Proxy Deployments](llm-proxy-deployments.md) + +- [Create and deploy a new LLM proxy deployment](llm-proxy-deployments.md#create-and-deploy-a-new-llm-proxy-deployment) +- [Get deployments for an LLM proxy](llm-proxy-deployments.md#get-deployments-for-an-llm-proxy) +- [Get LLM proxy deployment by ID](llm-proxy-deployments.md#get-llm-proxy-deployment-by-id) +- [Delete LLM proxy deployment](llm-proxy-deployments.md#delete-llm-proxy-deployment) +- [Undeploy LLM proxy deployment from gateway](llm-proxy-deployments.md#undeploy-llm-proxy-deployment-from-gateway) +- [Restore a previous LLM proxy deployment](llm-proxy-deployments.md#restore-a-previous-llm-proxy-deployment) + +### [API Keys](api-keys.md) + +- [List API keys for the current user](api-keys.md#list-api-keys-for-the-current-user) + +### [MCP Proxies](mcp-proxies.md) + +- [Create a new MCP proxy](mcp-proxies.md#create-a-new-mcp-proxy) +- [List all MCP proxies](mcp-proxies.md#list-all-mcp-proxies) +- [Get MCP proxy by unique identifier](mcp-proxies.md#get-mcp-proxy-by-unique-identifier) +- [Update an existing MCP proxy](mcp-proxies.md#update-an-existing-mcp-proxy) +- [Delete an MCP proxy](mcp-proxies.md#delete-an-mcp-proxy) +- [Fetch server info from MCP proxy backend services](mcp-proxies.md#fetch-server-info-from-mcp-proxy-backend-services) + +### [MCP Proxy Deployments](mcp-proxy-deployments.md) + +- [Create and deploy a new deployment for MCP proxy](mcp-proxy-deployments.md#create-and-deploy-a-new-deployment-for-mcp-proxy) +- [Get deployments for an MCP proxy](mcp-proxy-deployments.md#get-deployments-for-an-mcp-proxy) +- [Get deployment by ID](mcp-proxy-deployments.md#get-deployment-by-id) +- [Delete deployment](mcp-proxy-deployments.md#delete-deployment) +- [Undeploy deployment from gateway](mcp-proxy-deployments.md#undeploy-deployment-from-gateway) +- [Restore a previous deployment](mcp-proxy-deployments.md#restore-a-previous-deployment) + +### [SubscriptionPlans](subscriptionplans.md) + +- [Create subscription plan](subscriptionplans.md#create-subscription-plan) +- [List subscription plans](subscriptionplans.md#list-subscription-plans) +- [Get subscription plan by ID](subscriptionplans.md#get-subscription-plan-by-id) +- [Update subscription plan](subscriptionplans.md#update-subscription-plan) +- [Delete subscription plan](subscriptionplans.md#delete-subscription-plan) + +### [Subscriptions](subscriptions.md) + +- [Create subscription](subscriptions.md#create-subscription) +- [List subscriptions](subscriptions.md#list-subscriptions) +- [Get subscription by ID](subscriptions.md#get-subscription-by-id) +- [Update subscription](subscriptions.md#update-subscription) +- [Delete subscription](subscriptions.md#delete-subscription) + +### [Secrets](secrets.md) + +- [Create a secret](secrets.md#create-a-secret) +- [List secrets](secrets.md#list-secrets) +- [Get a secret by handle](secrets.md#get-a-secret-by-handle) +- [Rotate a secret value](secrets.md#rotate-a-secret-value) +- [Delete a secret](secrets.md#delete-a-secret) + +### [Schemas](schemas.md) + diff --git a/docs/rest-apis/platform-api/api-keys.md b/docs/rest-apis/platform-api/api-keys.md new file mode 100644 index 0000000000..e0c7400b62 --- /dev/null +++ b/docs/rest-apis/platform-api/api-keys.md @@ -0,0 +1,113 @@ +

API Keys

+ +API key management operations for REST APIs and LLM Providers + +## List API keys for the current user + + + +`GET /me/api-keys` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/me/api-keys \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Returns API keys created by the caller within the organization. +Optionally filter by one or more artifact types using a comma-separated `type` query parameter. +The plain key value is never returned. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|type|query|array[string]|false|Comma-separated list of artifact types to filter by.| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +#### Detailed descriptions + +**type**: Comma-separated list of artifact types to filter by. +If omitted, all types are returned. + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|type|RestApi| +|type|LlmProvider| +|type|LlmProxy| + +> Example responses + +> 200 Response + +```json +{ + "list": [ + { + "id": "string", + "displayName": "string", + "maskedApiKey": "string", + "status": "active", + "createdAt": "2019-08-24T14:15:22Z", + "createdBy": "john.doe", + "updatedAt": "2019-08-24T14:15:22Z", + "expiresAt": "2019-08-24T14:15:22Z", + "issuer": "api-platform-devportal", + "allowedTargets": "string", + "artifactId": "wso2-openai-provider", + "artifactType": "RestApi" + } + ], + "count": 0, + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of API keys retrieved successfully|[UserAPIKeyListResponse](schemas.md#schemauserapikeylistresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/applications.md b/docs/rest-apis/platform-api/applications.md new file mode 100644 index 0000000000..cb8a7fd245 --- /dev/null +++ b/docs/rest-apis/platform-api/applications.md @@ -0,0 +1,1548 @@ +

Applications

+ +Application management operations + +## Create a new application + + + +`POST /applications` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/applications \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Creates a new application within the organization specified in the JWT token. + +> Payload + +```json +{ + "id": "my-app-handle", + "displayName": "GenAI Demo App", + "projectId": "default-project", + "type": "genai", + "description": "Sample GenAI application" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[CreateApplicationRequest](schemas.md#schemacreateapplicationrequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "id": "my-app-handle", + "displayName": "GenAI Demo App", + "projectId": "default-project", + "type": "genai", + "description": "Sample GenAI application", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Application created successfully|[Application](schemas.md#schemaapplication)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## Get applications for current user's organization + + + +`GET /applications` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/applications?projectId=default-project \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves applications belonging to the organization specified in the JWT token. +Filters by project using the required `projectId` query parameter. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|projectId|query|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by.| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| +|sortBy|query|string|false|Field to sort the collection by. An unrecognized value falls back to the default sort (createdAt).| +|sortOrder|query|string|false|Sort direction applied to `sortBy`.| +|query|query|string|false|Case-insensitive substring filter matched against the resource id (handle).| + +#### Detailed descriptions + +**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by. + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|sortBy|name| +|sortBy|createdAt| +|sortOrder|asc| +|sortOrder|desc| + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "id": "my-app-handle", + "displayName": "GenAI Demo App", + "projectId": "default-project", + "type": "genai", + "description": "Sample GenAI application", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Applications retrieved successfully|[ApplicationListResponse](schemas.md#schemaapplicationlistresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get application by handle + + + +`GET /applications/{applicationId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/applications/{applicationId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves a specific application by handle. Access is validated against +the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|applicationId|path|string|true|**Application ID** consisting of the **handle** of the application.| + +#### Detailed descriptions + +**applicationId**: **Application ID** consisting of the **handle** of the application. + +> Example responses + +> 200 Response + +```json +{ + "id": "my-app-handle", + "displayName": "GenAI Demo App", + "projectId": "default-project", + "type": "genai", + "description": "Sample GenAI application", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Application retrieved successfully|[Application](schemas.md#schemaapplication)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Update application + + + +`PUT /applications/{applicationId}` + +> Code samples + +```shell + +curl -X PUT https://localhost:9243/api/v0.9/applications/{applicationId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Updates an existing application by handle. + +> Payload + +```json +{ + "id": "my-app-handle", + "displayName": "GenAI Demo App", + "projectId": "default-project", + "type": "genai", + "description": "Sample GenAI application" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|applicationId|path|string|true|**Application ID** consisting of the **handle** of the application.| +|body|body|[Application](schemas.md#schemaapplication)|true|none| + +#### Detailed descriptions + +**applicationId**: **Application ID** consisting of the **handle** of the application. + +> Example responses + +> 200 Response + +```json +{ + "id": "my-app-handle", + "displayName": "GenAI Demo App", + "projectId": "default-project", + "type": "genai", + "description": "Sample GenAI application", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Application updated successfully|[Application](schemas.md#schemaapplication)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete application + + + +`DELETE /applications/{applicationId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/applications/{applicationId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Deletes an existing application by handle. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|applicationId|path|string|true|**Application ID** consisting of the **handle** of the application.| + +#### Detailed descriptions + +**applicationId**: **Application ID** consisting of the **handle** of the application. + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Application deleted successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## List application API key mappings + + + +`GET /applications/{applicationId}/api-keys` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/applications/{applicationId}/api-keys \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Lists all API keys mapped to the specified application. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|applicationId|path|string|true|**Application ID** consisting of the **handle** of the application.| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +#### Detailed descriptions + +**applicationId**: **Application ID** consisting of the **handle** of the application. + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "keyId": "client-key-1", + "associatedEntity": { + "id": "pizza-api", + "kind": "RestApi" + }, + "status": "ACTIVE", + "userId": "john.doe", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z", + "expiresAt": "2026-11-15T10:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Mapped API keys retrieved successfully|[MappedAPIKeyListResponse](schemas.md#schemamappedapikeylistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Add application API key mappings + + + +`POST /applications/{applicationId}/api-keys` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/applications/{applicationId}/api-keys \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Adds API key mappings to the specified application. + +> Payload + +```json +{ + "apiKeys": [ + { + "keyId": "client-key-1", + "associatedEntity": { + "id": "pizza-api" + } + } + ] +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|applicationId|path|string|true|**Application ID** consisting of the **handle** of the application.| +|body|body|[AddApplicationAPIKeysRequest](schemas.md#schemaaddapplicationapikeysrequest)|true|none| + +#### Detailed descriptions + +**applicationId**: **Application ID** consisting of the **handle** of the application. + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "keyId": "client-key-1", + "associatedEntity": { + "id": "pizza-api", + "kind": "RestApi" + }, + "status": "ACTIVE", + "userId": "john.doe", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z", + "expiresAt": "2026-11-15T10:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key mappings added successfully|[MappedAPIKeyListResponse](schemas.md#schemamappedapikeylistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Remove application API key mapping + + + +`DELETE /applications/{applicationId}/api-keys/{apiKeyId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/applications/{applicationId}/api-keys/{apiKeyId}?entityID=my-rest-api-handle \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Removes a mapped API key from the specified application. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|applicationId|path|string|true|**Application ID** consisting of the **handle** of the application.| +|apiKeyId|path|string|true|**API Key ID** consisting of the **name** of the API key.| +|entityID|query|string|true|**Entity ID** of the artifact associated with the API key mapping.| + +#### Detailed descriptions + +**applicationId**: **Application ID** consisting of the **handle** of the application. + +**apiKeyId**: **API Key ID** consisting of the **name** of the API key. + +**entityID**: **Entity ID** of the artifact associated with the API key mapping. + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|API key mapping removed successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## List application associations + + + +`GET /applications/{applicationId}/associations` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/applications/{applicationId}/associations \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Lists association targets mapped to the specified application. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|applicationId|path|string|true|**Application ID** consisting of the **handle** of the application.| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +#### Detailed descriptions + +**applicationId**: **Application ID** consisting of the **handle** of the application. + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "id": "provider-handle", + "displayName": "OpenAI Provider", + "version": "v1.0", + "kind": "LlmProvider", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Application associations retrieved successfully|[ApplicationAssociationListResponse](schemas.md#schemaapplicationassociationlistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Add application associations + + + +`POST /applications/{applicationId}/associations` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/applications/{applicationId}/associations \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Adds association targets to the specified application. + +> Payload + +```json +{ + "associations": [ + { + "id": "provider-handle", + "kind": "LlmProvider" + } + ] +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|applicationId|path|string|true|**Application ID** consisting of the **handle** of the application.| +|body|body|[AddApplicationAssociationsRequest](schemas.md#schemaaddapplicationassociationsrequest)|true|none| + +#### Detailed descriptions + +**applicationId**: **Application ID** consisting of the **handle** of the application. + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "id": "provider-handle", + "displayName": "OpenAI Provider", + "version": "v1.0", + "kind": "LlmProvider", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Application associations added successfully|[ApplicationAssociationListResponse](schemas.md#schemaapplicationassociationlistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Remove application association + + + +`DELETE /applications/{applicationId}/associations/{associationId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/applications/{applicationId}/associations/{associationId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Removes an association target from the specified application. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|applicationId|path|string|true|**Application ID** consisting of the **handle** of the application.| +|associationId|path|string|true|**Association ID** consisting of the **handle** or **UUID** of the association target.| + +#### Detailed descriptions + +**applicationId**: **Application ID** consisting of the **handle** of the application. + +**associationId**: **Association ID** consisting of the **handle** or **UUID** of the association target. + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Application association removed successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## List application API key mappings for an association + + + +`GET /applications/{applicationId}/associations/{associationId}/api-keys` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/applications/{applicationId}/associations/{associationId}/api-keys \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Lists API keys mapped to the specified application for the given associated target. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|applicationId|path|string|true|**Application ID** consisting of the **handle** of the application.| +|associationId|path|string|true|**Association ID** consisting of the **handle** or **UUID** of the association target.| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +#### Detailed descriptions + +**applicationId**: **Application ID** consisting of the **handle** of the application. + +**associationId**: **Association ID** consisting of the **handle** or **UUID** of the association target. + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "keyId": "client-key-1", + "associatedEntity": { + "id": "pizza-api", + "kind": "RestApi" + }, + "status": "ACTIVE", + "userId": "john.doe", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z", + "expiresAt": "2026-11-15T10:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Mapped API keys retrieved successfully|[MappedAPIKeyListResponse](schemas.md#schemamappedapikeylistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/authentication.md b/docs/rest-apis/platform-api/authentication.md new file mode 100644 index 0000000000..f26ecddd4c --- /dev/null +++ b/docs/rest-apis/platform-api/authentication.md @@ -0,0 +1,358 @@ +# Authentication + +## Overview + +Every Platform API request is authenticated with a **Bearer JWT** in the `Authorization` header and +authorized against the **OAuth2 scopes** carried in that token. There is no Basic Auth path and no +API-key path for this API — the access token is the only credential. + +``` +Authorization: Bearer +``` + +Two things must be true for a request to succeed: + +1. **Authentication** — the token's signature, expiry and issuer validate, and it carries an + `organization` claim identifying the caller's organization. +2. **Authorization** — the token's `scope` claim (or, in role mode, its roles expanded to scopes) + satisfies the scope declared on that specific operation. Every operation page in this + documentation lists its required scopes under an **Authentication** heading. + +All operations are automatically scoped to the organization in the token. An organization ID is +never read from a path, query or body parameter, so a token for one organization can never reach +another organization's resources. + +## Authentication Modes + +The mode is selected once at startup with `[platform_api.auth] mode`. Only the selected mode's +configuration section is read; the others are ignored. + +| Mode | How tokens are obtained | Signature verified with | Typical use | +|---|---|---|---| +| `file` | The built-in login endpoint issues RS256 JWTs for locally configured users | `auth.jwt.public_key_file` | Quickstart, air-gapped setup | +| `external_token` | Tokens are minted outside the Platform API by a trusted issuer | `auth.jwt.public_key_file` | Existing token service | +| `idp` | An external Identity Provider's token endpoint | The IDP's JWKS (`auth.idp.jwks_url`) | Production (Asgardeo, Keycloak, Auth0, Entra ID, WSO2 IS) | + +**Supported signing algorithms**: `RS256`, `RS384`, `RS512` (RSA) and, in IDP mode, `ES256`, +`ES384`, `ES512` (ECDSA). Symmetric (`HS*`) and unsigned (`none`) tokens are always rejected. + +### Mode: `idp` (recommended for production) + +Tokens are validated against the IDP's JWKS endpoint. `jwks_url` and `issuer` are required. + +```toml +[platform_api.auth] +mode = "idp" +scope_validation = true + +[platform_api.auth.idp] +name = "asgardeo" +jwks_url = "https://accounts.example.com/oauth2/jwks" +issuer = ["https://accounts.example.com"] # list of accepted issuers +audience = [] # accepted "aud" values; empty skips the audience check +validation_mode = "scope" # "scope" (default) or "role" +role_mappings = "" # path to a role→scope YAML file, used when validation_mode = "role" +``` + +Obtain a token from the IDP as usual (client credentials, authorization code, …) and send it as a +Bearer token. The Platform API never issues tokens in this mode. + +### Mode: `file` (local users) + +The login endpoint authenticates a username/password against `[[platform_api.auth.file.users]]` and +issues an RS256 JWT signed with `auth.jwt.private_key_file`. Suitable for initial or air-gapped +setup — prefer an IDP in production. + +```toml +[platform_api.auth] +mode = "file" + +[platform_api.auth.file.organization] +id = "default" +display_name = "Default" +uuid = "99089a17-72e0-4dd8-a2f4-c8dfbb085295" # emitted as the `organization` claim + +[[platform_api.auth.file.users]] +username = "admin" +password_hash = "$2a$12$..." # bcrypt; htpasswd -bnBC 12 "" | tr -d ':\n' +scopes = "ap:organization:manage ap:rest_api:manage ap:gateway:manage" + +[platform_api.auth.jwt] +issuer = "platform-api" +public_key_file = "/etc/platform-api/keys/jwt_public.pem" +private_key_file = "/etc/platform-api/keys/jwt_private.pem" +token_ttl = "1h" +``` + +The user's `scopes` string becomes the `scope` claim of the issued token — trim it to restrict what +that user can do. + +#### Obtaining a token + +`POST /api/portal/v0.9/auth/login` (form-encoded; this path bypasses the auth middleware): + +```shell +curl -X POST https://localhost:9243/api/portal/v0.9/auth/login \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -d 'username=admin' \ + -d 'password=' +``` + +> 200 Response + +```json +{ + "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", + "expires_at": 1767225600 +} +``` + +Use `token` as the Bearer credential on every subsequent call. Its lifetime is `auth.jwt.token_ttl`. + +### Mode: `external_token` + +Tokens are minted by a trusted external issuer and only *verified* here, using +`auth.jwt.public_key_file`. The Platform API does not sign anything in this mode, and +`private_key_file` is not required. Token expiry is whatever `exp` the issuer set. + +## Required Claims + +| Claim | Required | Purpose | +|---|---|---| +| `organization` | Yes | UUID of the caller's organization. Every operation is scoped to it. A token without it is rejected. | +| `scope` | Yes (in `scope` validation mode) | Space-separated list of granted scopes, checked against the operation's declared scopes. | +| `iss` | Yes | Must match the configured issuer (`auth.jwt.issuer`, or one of `auth.idp.issuer`). | +| `exp` | Yes | Expired tokens are rejected. | +| `sub` | Yes | The user's unique ID. | +| `aud` | Conditional | Enforced in IDP mode when `auth.idp.audience` is non-empty. | +| `org_name`, `org_handle`, `username`, `email` | No | Identity metadata surfaced to handlers. | + +### Claim name mappings + +Claim names are configurable, so the same mapping is used for both issuing and validating tokens +and the two can never drift apart. Each value is either a flat claim name (`org_id`) or a +dot-separated path into a nested claim (`realm_access.roles`) — useful for IDPs like Keycloak. + +```toml +[platform_api.auth.claim_mappings] +organization = "organization" # claim carrying the org ID +org_name = "org_name" +org_handle = "org_handle" +user_id = "sub" +username = "username" +email = "email" +scope = "scope" # space-separated scope string +roles = "" # e.g. "realm_access.roles" (Keycloak) or "roles" (Asgardeo) +``` + +## Authorization + +### Scope naming convention + +Scopes follow `ap:[:]:`: + +| Form | Grants | +|---|---| +| `ap:rest_api:read` | A single action on a resource | +| `ap:rest_api:manage` | Every action on that resource (read, create, update, delete) | +| `ap:rest_api:deployment:manage` | Every action on the **subresource** only — not on the parent `rest_api` | + +An operation may declare several acceptable scopes. The token needs **at least one** of them — +which is why `ap:rest_api:create` and `ap:rest_api:manage` typically both appear on a create +operation. + +### Validation modes + +`auth.idp.validation_mode` selects how effective scopes are derived: + +- **`scope`** (default) — the token's `scope` claim is used directly. +- **`role`** — the roles found at `claim_mappings.roles` are expanded into scopes via the YAML file + at `auth.idp.role_mappings`. When a token carries several roles, the effective scopes are the + **union** of all matching role entries (most permissive wins). + +```yaml +# role_mappings YAML — requires a server restart to take effect +roles: + - name: platform-admin + scopes: + - ap:organization:manage + - ap:rest_api:manage + - ap:gateway:manage + - name: platform-operator + scopes: + - ap:organization:read + - ap:rest_api:read +``` + +### Disabling scope checks + +```toml +[platform_api.auth] +scope_validation = false +``` + +This authenticates callers but performs **no** authorization checks. Use it only to temporarily +bypass authorization during development — never in a deployed environment. + +### Unauthenticated paths + +`[platform_api.auth] skip_paths` lists path prefixes that bypass the auth middleware entirely — +health and metrics probes, the file-mode login endpoint, and the internal gateway routes (which +authenticate with a gateway token instead). Setting this key **replaces** the built-in default +list, so keep any entries you still need. + +## Error Responses + +Authentication and authorization failures are deliberately uniform — the response never reveals +*why* a credential was rejected. The specific reason is logged internally with a tracking ID. + +| Status | Meaning | Cause | +|---|---|---| +| 401 | Unauthorized | Missing `Authorization` header, non-Bearer header, malformed/expired/invalid-signature token, wrong issuer, or missing `organization` claim | +| 403 | Forbidden | Token is valid, but its effective scopes do not satisfy the operation's required scopes | + +A `401` carries the identical payload for every one of those causes, so it cannot be used to probe +which part of a credential was wrong. + +## Troubleshooting + +- **Every request returns 401 after switching to IDP mode** — check that `jwks_url` is reachable + from the Platform API and that the token's `iss` is listed in `auth.idp.issuer`. +- **401 with a token that the IDP says is valid** — the `organization` claim is probably missing or + under a different name. Point `claim_mappings.organization` at the claim your IDP actually emits. +- **401 only for some clients in IDP mode** — `auth.idp.audience` is non-empty and those tokens were + minted for a different client. Add the audience value or leave the list empty to skip the check. +- **403 on operations the user should be able to perform** — compare the token's `scope` claim + against the scopes listed on that operation's page. In `role` mode, confirm the role name in the + token matches an entry in the `role_mappings` file (it requires a restart after editing). +- **403 in `role` mode on every operation** — `claim_mappings.roles` is unset, so no roles are read + from the token. Set it to the claim path your IDP uses (`realm_access.roles` for Keycloak, + `roles` for Asgardeo/Entra ID). +- **Login endpoint returns 401 in `file` mode** — the stored `password_hash` must be a bcrypt hash; + a plaintext password there will never match. + +## Scope Reference + +The complete set of scopes defined by this API, as declared in the OpenAPI `OAuth2Security` scheme. + +- OAuth2, flow: `clientCredentials` +- Token URL (file mode): [https://localhost:9243/oauth2/token](https://localhost:9243/oauth2/token) + +|Scope|Scope Description| +|---|---| +|ap:api_key:read|Read API keys owned by the current user| +|ap:application:api_key:create|Create an application API key| +|ap:application:api_key:delete|Delete an application API key| +|ap:application:api_key:manage|Full access to application API keys| +|ap:application:api_key:read|Read application API keys| +|ap:application:association:api_key:read|Read API keys for an application association| +|ap:application:association:create|Create an application association| +|ap:application:association:delete|Delete an application association| +|ap:application:association:manage|Full access to application associations| +|ap:application:association:read|Read application associations| +|ap:application:create|Create an application| +|ap:application:delete|Delete an application| +|ap:application:manage|Full access to applications| +|ap:application:read|Read applications| +|ap:application:update|Update an application| +|ap:gateway:create|Create a gateway| +|ap:gateway:delete|Delete a gateway| +|ap:gateway:manage|Full access to gateways| +|ap:gateway:manifest:read|Read a gateway manifest| +|ap:gateway:read|Read gateways| +|ap:gateway_custom_policy:create|Create a gateway custom policy| +|ap:gateway_custom_policy:delete|Delete a gateway custom policy| +|ap:gateway_custom_policy:manage|Full access to gateway custom policies| +|ap:gateway_custom_policy:read|Read gateway custom policies| +|ap:gateway:token:create|Create a gateway token| +|ap:gateway:token:delete|Delete a gateway token| +|ap:gateway:token:manage|Full access to gateway tokens| +|ap:gateway:token:read|Read gateway tokens| +|ap:gateway:update|Update a gateway| +|ap:llm_provider:api_key:create|Create an LLM provider API key| +|ap:llm_provider:api_key:delete|Delete an LLM provider API key| +|ap:llm_provider:api_key:manage|Full access to LLM provider API keys| +|ap:llm_provider:api_key:read|Read LLM provider API keys| +|ap:llm_provider:create|Create an LLM provider| +|ap:llm_provider:delete|Delete an LLM provider| +|ap:llm_provider:deployment:create|Deploy an LLM provider| +|ap:llm_provider:deployment:delete|Delete an LLM provider deployment| +|ap:llm_provider:deployment:manage|Full access to LLM provider deployments| +|ap:llm_provider:deployment:read|Read LLM provider deployments| +|ap:llm_provider:deployment:restore|Restore an LLM provider deployment| +|ap:llm_provider:deployment:undeploy|Undeploy an LLM provider deployment| +|ap:llm_provider:manage|Full access to LLM providers| +|ap:llm_provider:read|Read LLM providers| +|ap:llm_provider:update|Update an LLM provider| +|ap:llm_proxy:api_key:create|Create an LLM proxy API key| +|ap:llm_proxy:api_key:delete|Delete an LLM proxy API key| +|ap:llm_proxy:api_key:manage|Full access to LLM proxy API keys| +|ap:llm_proxy:api_key:read|Read LLM proxy API keys| +|ap:llm_proxy:create|Create an LLM proxy| +|ap:llm_proxy:delete|Delete an LLM proxy| +|ap:llm_proxy:deployment:create|Deploy an LLM proxy| +|ap:llm_proxy:deployment:delete|Delete an LLM proxy deployment| +|ap:llm_proxy:deployment:manage|Full access to LLM proxy deployments| +|ap:llm_proxy:deployment:read|Read LLM proxy deployments| +|ap:llm_proxy:deployment:restore|Restore an LLM proxy deployment| +|ap:llm_proxy:deployment:undeploy|Undeploy an LLM proxy deployment| +|ap:llm_proxy:manage|Full access to LLM proxies| +|ap:llm_proxy:read|Read LLM proxies| +|ap:llm_proxy:update|Update an LLM proxy| +|ap:llm_template:create|Create an LLM provider template| +|ap:llm_template:delete|Delete an LLM provider template| +|ap:llm_template:manage|Full access to LLM provider templates| +|ap:llm_template:read|Read LLM provider templates| +|ap:llm_template:update|Update an LLM provider template| +|ap:mcp_proxy:create|Create an MCP proxy| +|ap:mcp_proxy:delete|Delete an MCP proxy| +|ap:mcp_proxy:deployment:create|Deploy an MCP proxy| +|ap:mcp_proxy:deployment:delete|Delete an MCP proxy deployment| +|ap:mcp_proxy:deployment:manage|Full access to MCP proxy deployments| +|ap:mcp_proxy:deployment:read|Read MCP proxy deployments| +|ap:mcp_proxy:deployment:restore|Restore an MCP proxy deployment| +|ap:mcp_proxy:deployment:undeploy|Undeploy an MCP proxy deployment| +|ap:mcp_proxy:manage|Full access to MCP proxies| +|ap:mcp_proxy:read|Read MCP proxies| +|ap:mcp_proxy:update|Update an MCP proxy| +|ap:organization:create|Create an organization| +|ap:organization:manage|Full access to an organization| +|ap:organization:read|Read an organization| +|ap:project:create|Create a project| +|ap:project:delete|Delete a project| +|ap:project:manage|Full access to projects| +|ap:project:read|Read projects| +|ap:project:update|Update a project| +|ap:rest_api:api_key:create|Create an API key for a REST API| +|ap:rest_api:api_key:delete|Delete an API key of a REST API| +|ap:rest_api:api_key:manage|Full access to a REST API's API keys| +|ap:rest_api:api_key:update|Update an API key of a REST API| +|ap:rest_api:create|Create a REST API| +|ap:rest_api:delete|Delete a REST API| +|ap:rest_api:deployment:create|Deploy a REST API| +|ap:rest_api:deployment:delete|Delete a REST API deployment| +|ap:rest_api:deployment:manage|Full access to REST API deployments| +|ap:rest_api:deployment:read|Read REST API deployments| +|ap:rest_api:deployment:restore|Restore a REST API deployment| +|ap:rest_api:deployment:undeploy|Undeploy a REST API deployment| +|ap:rest_api:gateway:create|Add gateways to a REST API| +|ap:rest_api:gateway:manage|Full access to a REST API's gateways| +|ap:rest_api:gateway:read|Read a REST API's gateways| +|ap:rest_api:manage|Full access to REST APIs| +|ap:rest_api:read|Read REST APIs| +|ap:rest_api:update|Update a REST API| +|ap:secret:create|Create a secret| +|ap:secret:delete|Delete a secret| +|ap:secret:manage|Full access to secrets| +|ap:secret:read|Read secrets| +|ap:secret:update|Update a secret| +|ap:subscription:create|Create a subscription| +|ap:subscription:delete|Delete a subscription| +|ap:subscription:manage|Full access to subscriptions| +|ap:subscription:read|Read subscriptions| +|ap:subscription:update|Update a subscription| +|ap:subscription_plan:create|Create a subscription plan| +|ap:subscription_plan:delete|Delete a subscription plan| +|ap:subscription_plan:manage|Full access to subscription plans| +|ap:subscription_plan:read|Read subscription plans| +|ap:subscription_plan:update|Update a subscription plan| diff --git a/docs/rest-apis/platform-api/gateway-policies.md b/docs/rest-apis/platform-api/gateway-policies.md new file mode 100644 index 0000000000..f198dd96fa --- /dev/null +++ b/docs/rest-apis/platform-api/gateway-policies.md @@ -0,0 +1,431 @@ +

Gateway Policies

+ +Custom gateway policy management operations + +## Get synced custom policies for the current organization + + + +`GET /gateway-custom-policies` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/gateway-custom-policies \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Returns all custom policies synced to the current organization (from the JWT `organization` claim). + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +> Example responses + +> 200 Response + +```json +{ + "count": 0, + "list": [ + { + "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "organizationUuid": "bc554ded-7e40-44a7-b397-48480793ad03", + "name": "rate-limit-custom", + "version": "1.0.0", + "description": "Custom rate limiting policy", + "policyDefinition": {}, + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of custom policies|[CustomPolicyListResponse](schemas.md#schemacustompolicylistresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Sync a custom policy from the gateway manifest + + + +`POST /gateway-custom-policies/sync` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/gateway-custom-policies/sync?gatewayId=prod-gateway-01&policyName=set-wso2-headers&policyVersion=1.0.0 \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Syncs a custom policy from the gateway manifest into the organization's custom policy registry. +Version-based rules apply: +- **New major version**: creates a new policy record (e.g. v1.x.x and v2.x.x coexist). +- **New minor version** (same major): updates the existing record (e.g. v1.1.0 → v1.2.0). +- **Patch version change** (same major.minor): not allowed. +- **Downgrade**: not allowed. +Policy names are case-insensitive. +After syncing, the policy can be applied to APIs in the organization. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|gatewayId|query|string|true|Handle (URL-friendly slug) of the gateway whose manifest contains the policy| +|policyName|query|string|true|Name of the custom policy (case-insensitive)| +|policyVersion|query|string|true|Version of the custom policy in MAJOR.MINOR.PATCH format| + +> Example responses + +> 200 Response + +```json +{ + "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "organizationUuid": "bc554ded-7e40-44a7-b397-48480793ad03", + "name": "rate-limit-custom", + "version": "1.0.0", + "description": "Custom rate limiting policy", + "policyDefinition": {}, + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 422 Response + +```json +{ + "status": "error", + "code": "POLICY_INVALID_STATE", + "message": "The policy is not a custom policy, or its manifest is unavailable." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Custom policy synced successfully|[CustomPolicyResponse](schemas.md#schemacustompolicyresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Policy is not a custom policy or manifest is unavailable|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get a specific custom policy version + + + +`GET /gateway-custom-policies/{gatewayCustomPolicyId}/versions/{version}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/gateway-custom-policies/{gatewayCustomPolicyId}/versions/{version} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Returns a custom policy by its UUID and version for the current organization. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|gatewayCustomPolicyId|path|string(uuid)|true|UUID of the custom policy record| +|version|path|string|true|Version of the custom policy (e.g. "1.0.0")| + +> Example responses + +> 200 Response + +```json +{ + "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "organizationUuid": "bc554ded-7e40-44a7-b397-48480793ad03", + "name": "rate-limit-custom", + "version": "1.0.0", + "description": "Custom rate limiting policy", + "policyDefinition": {}, + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Custom policy retrieved successfully|[CustomPolicyResponse](schemas.md#schemacustompolicyresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete a specific custom policy version + + + +`DELETE /gateway-custom-policies/{gatewayCustomPolicyId}/versions/{version}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/gateway-custom-policies/{gatewayCustomPolicyId}/versions/{version} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Deletes a custom policy by its UUID and version. The policy must not be in use by any APIs. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|gatewayCustomPolicyId|path|string(uuid)|true|UUID of the custom policy record| +|version|path|string|true|Version of the custom policy (e.g. "1.0.0")| + +> Example responses + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Custom policy deleted successfully|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/gateway-tokens.md b/docs/rest-apis/platform-api/gateway-tokens.md new file mode 100644 index 0000000000..5c4b72e542 --- /dev/null +++ b/docs/rest-apis/platform-api/gateway-tokens.md @@ -0,0 +1,358 @@ +

Gateway Tokens

+ +Gateway token rotation and revocation operations + +## List active gateway tokens + + + +`GET /gateways/{gatewayId}/tokens` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/gateways/{gatewayId}/tokens \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Returns all active tokens for the specified gateway. Token hashes and salts are never exposed. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|gatewayId|path|string|true|**Gateway ID** (handle — unique slug identifier) of the Gateway.| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +#### Detailed descriptions + +**gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway. + +> Example responses + +> 200 Response + +```json +{ + "count": 0, + "list": [ + { + "id": "abc12345-f678-90de-f123-456789abcdef", + "status": "active", + "createdAt": "2025-10-14T10:30:00Z", + "revokedAt": null + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of active tokens|[GatewayTokenListResponse](schemas.md#schemagatewaytokenlistresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Rotate gateway token + + + +`POST /gateways/{gatewayId}/tokens` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/gateways/{gatewayId}/tokens \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Generates a new authentication token for the gateway. The existing token remains active +to enable zero-downtime rotation. Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|gatewayId|path|string|true|**Gateway ID** (handle — unique slug identifier) of the Gateway.| + +#### Detailed descriptions + +**gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway. + +> Example responses + +> 201 Response + +```json +{ + "id": "def45678-g901-23hi-j456-789012klmnop", + "token": "nM8pQ1rT4vW7yZ0bC3eF6hI9kL2nO5qR8tU1wX4zA7cE0fH3jK6mP9sV2yB5dG8i", + "createdAt": "2025-10-15T14:20:00Z", + "message": "New token generated successfully. Old token remains active until revoked." +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|New token generated successfully|[TokenRotationResponse](schemas.md#schematokenrotationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## Revoke gateway token + + + +`DELETE /gateways/{gatewayId}/tokens/{tokenId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/gateways/{gatewayId}/tokens/{tokenId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Revokes a specific gateway token. Operation is idempotent - revoking an already-revoked +token succeeds. Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|gatewayId|path|string|true|**Gateway ID** (handle — unique slug identifier) of the Gateway.| +|tokenId|path|string(uuid)|true|**Token ID** consisting of the **UUID** of the Token.| + +#### Detailed descriptions + +**gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway. + +**tokenId**: **Token ID** consisting of the **UUID** of the Token. + +> Example responses + +> 200 Response + +```json +{ + "message": "Token revoked successfully" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Token revoked successfully|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» message|string|false|none|none| diff --git a/docs/rest-apis/platform-api/gateways.md b/docs/rest-apis/platform-api/gateways.md new file mode 100644 index 0000000000..db4501ea6f --- /dev/null +++ b/docs/rest-apis/platform-api/gateways.md @@ -0,0 +1,809 @@ +

Gateways

+ +Gateway registration and management operations + +## Register a new gateway + + + +`POST /gateways` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/gateways \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Creates a new gateway within the organization specified in the JWT token. +Organization ID is automatically extracted from the token and does not need to be provided. + +> Payload + +```json +{ + "id": "prod-gateway-01", + "displayName": "Production Gateway 01", + "description": "Production gateway for handling API traffic", + "endpoints": [ + "https://api.example.com:8443/api/v1", + "wss://events.example.com:8444" + ], + "isCritical": true, + "functionalityType": "regular", + "properties": { + "region": "us-west", + "tier": "premium" + }, + "version": "1.0" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[CreateGatewayRequest](schemas.md#schemacreategatewayrequest)|true|Gateway registration details| + +> Example responses + +> 201 Response + +```json +{ + "id": "prod-gateway-01", + "organizationId": "acme", + "displayName": "Production Gateway 01", + "description": "Production gateway for handling API traffic", + "properties": { + "region": "us-west", + "tier": "premium" + }, + "endpoints": [ + "https://api.example.com:8443/api/v1", + "wss://events.example.com:8444" + ], + "isCritical": true, + "functionalityType": "regular", + "version": "1.0", + "isActive": true, + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-10-14T10:30:00Z", + "updatedAt": "2025-10-14T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Gateway registered successfully|[GatewayResponse](schemas.md#schemagatewayresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## List all gateways + + + +`GET /gateways` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/gateways \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves a list of all registered gateways for the organization specified in the JWT token. +Organization ID is automatically extracted from the token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| +|sortBy|query|string|false|Field to sort the collection by. An unrecognized value falls back to the default sort (createdAt).| +|sortOrder|query|string|false|Sort direction applied to `sortBy`.| +|query|query|string|false|Case-insensitive substring filter matched against the resource id (handle).| + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|sortBy|name| +|sortBy|createdAt| +|sortOrder|asc| +|sortOrder|desc| + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "id": "prod-gateway-01", + "organizationId": "acme", + "displayName": "Production Gateway 01", + "description": "Production gateway for handling API traffic", + "properties": { + "region": "us-west", + "tier": "premium" + }, + "endpoints": [ + "https://api.example.com:8443/api/v1", + "wss://events.example.com:8444" + ], + "isCritical": true, + "functionalityType": "regular", + "version": "1.0", + "isActive": true, + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-10-14T10:30:00Z", + "updatedAt": "2025-10-14T10:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Gateways retrieved successfully|[GatewayListResponse](schemas.md#schemagatewaylistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get gateway by UUID + + + +`GET /gateways/{gatewayId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/gateways/{gatewayId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves a specific gateway by its UUID. Access is validated against the organization +in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|gatewayId|path|string|true|**Gateway ID** (handle — unique slug identifier) of the Gateway.| + +#### Detailed descriptions + +**gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway. + +> Example responses + +> 200 Response + +```json +{ + "id": "prod-gateway-01", + "organizationId": "acme", + "displayName": "Production Gateway 01", + "description": "Production gateway for handling API traffic", + "properties": { + "region": "us-west", + "tier": "premium" + }, + "endpoints": [ + "https://api.example.com:8443/api/v1", + "wss://events.example.com:8444" + ], + "isCritical": true, + "functionalityType": "regular", + "version": "1.0", + "isActive": true, + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-10-14T10:30:00Z", + "updatedAt": "2025-10-14T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Gateway retrieved successfully|[GatewayResponse](schemas.md#schemagatewayresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Update gateway + + + +`PUT /gateways/{gatewayId}` + +> Code samples + +```shell + +curl -X PUT https://localhost:9243/api/v0.9/gateways/{gatewayId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Updates an existing gateway's mutable fields (description). +Access is validated against the organization in the JWT token. + +> Payload + +```json +{ + "organizationId": "acme", + "displayName": "Production Gateway 01", + "description": "Production gateway for handling API traffic", + "properties": { + "region": "us-west", + "tier": "premium" + }, + "endpoints": [ + "https://api.example.com:8443/api/v1", + "wss://events.example.com:8444" + ], + "isCritical": true, + "functionalityType": "regular", + "version": "1.0", + "isActive": true, + "createdAt": "2025-10-14T10:30:00Z", + "updatedAt": "2025-10-14T10:30:00Z" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|gatewayId|path|string|true|**Gateway ID** (handle — unique slug identifier) of the Gateway.| +|body|body|[GatewayResponse](schemas.md#schemagatewayresponse)|true|Gateway object that needs to be updated| + +#### Detailed descriptions + +**gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway. + +> Example responses + +> 200 Response + +```json +{ + "id": "prod-gateway-01", + "organizationId": "acme", + "displayName": "Production Gateway 01", + "description": "Production gateway for handling API traffic", + "properties": { + "region": "us-west", + "tier": "premium" + }, + "endpoints": [ + "https://api.example.com:8443/api/v1", + "wss://events.example.com:8444" + ], + "isCritical": true, + "functionalityType": "regular", + "version": "1.0", + "isActive": true, + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-10-14T10:30:00Z", + "updatedAt": "2025-10-14T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Gateway updated successfully|[GatewayResponse](schemas.md#schemagatewayresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete gateway + + + +`DELETE /gateways/{gatewayId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/gateways/{gatewayId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Permanently deletes a gateway and all associated tokens (CASCADE). +Deletion is blocked if the gateway has active API deployments or WebSocket connections. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|gatewayId|path|string|true|**Gateway ID** (handle — unique slug identifier) of the Gateway.| + +#### Detailed descriptions + +**gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway. + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Gateway deleted successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get gateway policy manifest + + + +`GET /gateways/{gatewayId}/manifest` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/gateways/{gatewayId}/manifest \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Returns the policy manifest for the specified gateway. The manifest is populated by the gateway +controller when it connects to the platform API, and contains all installed policies. Custom +policies additionally include their full policy definition schema. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|gatewayId|path|string|true|**Gateway ID** (handle — unique slug identifier) of the Gateway.| + +#### Detailed descriptions + +**gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway. + +> Example responses + +> 200 Response + +```json +{ + "policies": [ + { + "name": "set-wso2-headers", + "version": "v0.8.0", + "description": "Sets WSO2-specific headers in the request and response.", + "isCustomPolicy": true, + "policyDefinition": {} + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Gateway policy manifest|[ManifestSyncResponse](schemas.md#schemamanifestsyncresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/llm-provider-deployments.md b/docs/rest-apis/platform-api/llm-provider-deployments.md new file mode 100644 index 0000000000..5fb7858d61 --- /dev/null +++ b/docs/rest-apis/platform-api/llm-provider-deployments.md @@ -0,0 +1,767 @@ +

LLM Provider Deployments

+ +LLM provider deployment operations + +## Create and deploy a new LLM provider deployment + + + +`POST /llm-providers/{llmProviderId}/deployments` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/llm-providers/{llmProviderId}/deployments \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Creates an immutable deployment artifact for an LLM provider and deploys it to a specified gateway. +Each deployment targets a single gateway. The providerId parameter is the LLM provider handle (identifier), +not the UUID. The operation returns a transitional DEPLOYING status. Final success or failure will be reported asynchronously via the deployment's status and statusReason once the gateway acknowledges. +Access is validated against the organization in the JWT token. + +> Payload + +```json +{ + "name": "v1.0-production", + "base": "current", + "gatewayId": "prod-gateway-01", + "metadata": {} +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderId|path|string|true|Unique identifier of the LLM provider| +|body|body|[DeployRequest](schemas.md#schemadeployrequest)|true|Deployment request with gateway ID, base reference, and metadata| + +> Example responses + +> 201 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|LLM provider deployed successfully|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## Get deployments for an LLM provider + + + +`GET /llm-providers/{llmProviderId}/deployments` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/llm-providers/{llmProviderId}/deployments \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves all deployment artifacts for a specific LLM provider. The providerId parameter is the +LLM provider handle (identifier), not the UUID. Supports filtering by gateway UUID and deployment status. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderId|path|string|true|Unique identifier of the LLM provider| +|gatewayId|query|string|false|**Gateway ID** consisting of the **UUID** of the Gateway to filter status by.| +|status|query|string|false|Filter deployments by status (DEPLOYED, UNDEPLOYED, DEPLOYING, UNDEPLOYING, FAILED, or ARCHIVED)| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +#### Detailed descriptions + +**gatewayId**: **Gateway ID** consisting of the **UUID** of the Gateway to filter status by. + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|status|DEPLOYED| +|status|UNDEPLOYED| +|status|DEPLOYING| +|status|UNDEPLOYING| +|status|FAILED| +|status|ARCHIVED| + +> Example responses + +> 200 Response + +```json +{ + "count": 0, + "list": [ + { + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Deployments retrieved successfully|[DeploymentListResponse](schemas.md#schemadeploymentlistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get LLM provider deployment by ID + + + +`GET /llm-providers/{llmProviderId}/deployments/{deploymentId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/llm-providers/{llmProviderId}/deployments/{deploymentId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves metadata for a specific LLM provider deployment artifact including status, gateway association, +and timestamps. Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderId|path|string|true|Unique identifier of the LLM provider| +|deploymentId|path|string(uuid)|true|The UUID of the deployment| + +> Example responses + +> 200 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Deployment metadata retrieved successfully|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete LLM provider deployment + + + +`DELETE /llm-providers/{llmProviderId}/deployments/{deploymentId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/llm-providers/{llmProviderId}/deployments/{deploymentId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Deletes a deployment artifact. Deletion is only allowed when the deployment is in UNDEPLOYED status. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderId|path|string|true|Unique identifier of the LLM provider| +|deploymentId|path|string(uuid)|true|The UUID of the deployment| + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Deployment deleted successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Undeploy LLM provider deployment from gateway + + + +`POST /llm-providers/{llmProviderId}/deployments/{deploymentId}/undeploy` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/llm-providers/{llmProviderId}/deployments/{deploymentId}/undeploy?gatewayId=string \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Undeploys an active LLM provider deployment, stopping it from being served on the specified gateway. +The deployment artifact remains in the system and can be restored later. +Returns the updated deployment object with initial status UNDEPLOYING. Final status (UNDEPLOYED or FAILED) will be reported asynchronously via the deployment's status and statusReason once the gateway acknowledges. + +The gatewayId query parameter is validated against the deployment's bound gateway to prevent unintended operations. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderId|path|string|true|Unique identifier of the LLM provider| +|deploymentId|path|string|true|UUID of the deployment to undeploy| +|gatewayId|query|string|true|UUID of the gateway (validated against deployment's bound gateway)| + +> Example responses + +> 200 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Undeploy initiated successfully. Returns the deployment with initial status UNDEPLOYING. Poll status for final result.|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Restore a previous LLM provider deployment + + + +`POST /llm-providers/{llmProviderId}/deployments/{deploymentId}/restore` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/llm-providers/{llmProviderId}/deployments/{deploymentId}/restore?gatewayId=string \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Initiates restoring a previous deployment (ARCHIVED or UNDEPLOYED) on the specified gateway. +Returns the deployment with initial status DEPLOYING. Final success or failure will be reported asynchronously via the deployment's status and statusReason once the gateway acknowledges. +The target deployment must not already be in DEPLOYED status. + +The gatewayId query parameter is validated against the deployment's bound gateway to prevent unintended operations. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderId|path|string|true|Unique identifier of the LLM provider| +|deploymentId|path|string|true|UUID of the deployment to restore (must be ARCHIVED or UNDEPLOYED)| +|gatewayId|query|string|true|UUID of the gateway (validated against deployment's bound gateway)| + +> Example responses + +> 200 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Restore initiated successfully. Returns the deployment with initial status DEPLOYING. Poll status for final result.|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/llm-provider-templates.md b/docs/rest-apis/platform-api/llm-provider-templates.md new file mode 100644 index 0000000000..9296538748 --- /dev/null +++ b/docs/rest-apis/platform-api/llm-provider-templates.md @@ -0,0 +1,1502 @@ +

LLM Provider Templates

+ +LLM provider template management operations + +## Create a new LLM provider template family + + + +`POST /llm-provider-templates` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/llm-provider-templates \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Creates a new template family, starting at v1.0, with an +`LLMProviderTemplate` body. To add a new version to an existing family, +use `POST /llm-provider-templates/copy` instead. Organization is +identified via the JWT token. + +> Payload + +```json +{ + "id": "openai", + "displayName": "OpenAI", + "managedBy": "wso2", + "description": "Default OpenAI template", + "version": "v1.0", + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "metadata": { + "endpointUrl": "https://api.openai.com", + "auth": { + "type": "bearer", + "header": "Authorization", + "valuePrefix": "Bearer " + }, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "openapiSpecUrl": "https://api.openai.com/openapi.json" + }, + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "resourceMappings": { + "resources": [ + { + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } + } + ] + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[LLMProviderTemplate](schemas.md#schemallmprovidertemplate)|true|The template family to create (starts at v1.0).| + +> Example responses + +> 201 Response + +```json +{ + "id": "openai", + "groupId": "openai", + "displayName": "OpenAI", + "managedBy": "wso2", + "description": "Default OpenAI template", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "readOnly": false, + "version": "v1.0", + "isLatest": true, + "enabled": true, + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "metadata": { + "endpointUrl": "https://api.openai.com", + "auth": { + "type": "bearer", + "header": "Authorization", + "valuePrefix": "Bearer " + }, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "openapiSpecUrl": "https://api.openai.com/openapi.json" + }, + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "resourceMappings": { + "resources": [ + { + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } + } + ] + }, + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|LLM provider template created successfully|[LLMProviderTemplate](schemas.md#schemallmprovidertemplate)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## List templates, list a family's versions, or get a single version + + + +`GET /llm-provider-templates` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/llm-provider-templates \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves LLM provider templates based on the `query` parameter: + - no `query` -> list all template versions + - `query=latest:true` -> list the latest version of each template + - `query=groupId:` -> list all versions for the template + - `query=groupId:&version:` -> retrieve a specific template version + +All filters are provided via the URL-encoded `query` parameter (e.g. +`?query=groupId%3Awso2-openai%26version%3Av2.0`). + +Returns a single `LLMProviderTemplate` only when both `groupId` and `version` are specified; +otherwise returns an `LLMProviderTemplateListResponse`. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|query|query|string|false|URL-encoded search DSL. `query=latest:true` lists only the latest version of each family; `query=groupId:` lists that family's versions; adding `&version:` returns the single full template for that version. Terms are `&`-separated `key:value` pairs and the whole value is percent-encoded (e.g. groupId%3Awso2-openai%26version%3Av2.0).| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +> Example responses + +> 200 Response + +```json +{ + "count": 5, + "list": [ + { + "id": "openai", + "groupId": "openai", + "displayName": "OpenAI", + "managedBy": "wso2", + "description": "Default OpenAI template", + "createdBy": "john.doe", + "version": "v1.0", + "isLatest": true, + "enabled": true, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "createdAt": "2025-11-25T10:30:00Z", + "updatedAt": "2025-11-25T10:30:00Z", + "readOnly": false + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Without a `version` term: a list response — either templates (all versions by default, or the latest of each family when `query=latest:true`) or a family's versions. With `query=groupId:&version:`: the single full template for that version.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +

Response Schema

+ +#### Enumerated Values + +|Property|Value| +|---|---| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| + +## Create a new version by copying an existing one + + + +`POST /llm-provider-templates/copy` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/llm-provider-templates/copy?fromTemplateId=openai-v3-0&toVersion=v4.0 \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Creates a new version within a template family by cloning an existing +version (`fromTemplateId`) and applying optional field overrides from +the body. The new version lands in the same family as the source and +becomes the latest. + +Versions start at v1.0 and only go higher — `toVersion` must match the +`v.` pattern with a major of at least 1 (e.g. v2.0); v0.x +is rejected. `toTemplateId`, when supplied, must equal the handle +derived from the family and `toVersion`. Organization is identified via +the JWT token. + +Built-in (WSO2-managed) template families are immutable: adding a version +to one is rejected with `403`. To base a custom template on a built-in, +create a new template (`POST /llm-provider-templates`) instead — it gets +its own `group_id` and starts at v1.0. + +> Payload + +```json +{ + "displayName": "OpenAI", + "version": "v2.0", + "managedBy": "organization", + "description": "Default OpenAI template", + "openapi": "string", + "metadata": { + "endpointUrl": "https://api.openai.com", + "auth": { + "type": "bearer", + "header": "Authorization", + "valuePrefix": "Bearer " + }, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "openapiSpecUrl": "https://api.openai.com/openapi.json" + }, + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "resourceMappings": { + "resources": [ + { + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } + } + ] + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|fromTemplateId|query|string|true|Handle (id) of the source version to copy from.| +|toTemplateId|query|string|false|Expected handle of the new version. Must equal the handle derived from the source family and toVersion; used to validate the target.| +|toVersion|query|string|true|New version identifier, e.g. v4.0. Must match v. with a major of at least 1, and be unique within the family.| +|body|body|[CreateLLMProviderTemplateVersionRequest](schemas.md#schemacreatellmprovidertemplateversionrequest)|false|Optional overrides applied on top of the copied config. Any field present replaces the value cloned from the source version.| + +> Example responses + +> 201 Response + +```json +{ + "id": "openai", + "groupId": "openai", + "displayName": "OpenAI", + "managedBy": "wso2", + "description": "Default OpenAI template", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "readOnly": false, + "version": "v1.0", + "isLatest": true, + "enabled": true, + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "metadata": { + "endpointUrl": "https://api.openai.com", + "auth": { + "type": "bearer", + "header": "Authorization", + "valuePrefix": "Bearer " + }, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "openapiSpecUrl": "https://api.openai.com/openapi.json" + }, + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "resourceMappings": { + "resources": [ + { + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } + } + ] + }, + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|New version created successfully|[LLMProviderTemplate](schemas.md#schemallmprovidertemplate)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## Get LLM provider template by id + + + +`GET /llm-provider-templates/{llmProviderTemplateId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/llm-provider-templates/{llmProviderTemplateId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieve the complete configuration for a specific LLM provider template. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderTemplateId|path|string|true|Unique handle of the template.| + +> Example responses + +> 200 Response + +```json +{ + "id": "openai", + "groupId": "openai", + "displayName": "OpenAI", + "managedBy": "wso2", + "description": "Default OpenAI template", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "readOnly": false, + "version": "v1.0", + "isLatest": true, + "enabled": true, + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "metadata": { + "endpointUrl": "https://api.openai.com", + "auth": { + "type": "bearer", + "header": "Authorization", + "valuePrefix": "Bearer " + }, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "openapiSpecUrl": "https://api.openai.com/openapi.json" + }, + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "resourceMappings": { + "resources": [ + { + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } + } + ] + }, + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM provider template details|[LLMProviderTemplate](schemas.md#schemallmprovidertemplate)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Update an existing LLM provider template + + + +`PUT /llm-provider-templates/{llmProviderTemplateId}` + +> Code samples + +```shell + +curl -X PUT https://localhost:9243/api/v0.9/llm-provider-templates/{llmProviderTemplateId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update the configuration of an existing LLM provider template. The template +is identified by its unique handle (id). This operation updates the latest +version of the template. + +> Payload + +```json +{ + "id": "openai", + "displayName": "OpenAI", + "managedBy": "wso2", + "description": "Default OpenAI template", + "version": "v1.0", + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "metadata": { + "endpointUrl": "https://api.openai.com", + "auth": { + "type": "bearer", + "header": "Authorization", + "valuePrefix": "Bearer " + }, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "openapiSpecUrl": "https://api.openai.com/openapi.json" + }, + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "resourceMappings": { + "resources": [ + { + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } + } + ] + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderTemplateId|path|string|true|Unique identifier of the template to update| +|body|body|[LLMProviderTemplate](schemas.md#schemallmprovidertemplate)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "id": "openai", + "groupId": "openai", + "displayName": "OpenAI", + "managedBy": "wso2", + "description": "Default OpenAI template", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "readOnly": false, + "version": "v1.0", + "isLatest": true, + "enabled": true, + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "metadata": { + "endpointUrl": "https://api.openai.com", + "auth": { + "type": "bearer", + "header": "Authorization", + "valuePrefix": "Bearer " + }, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "openapiSpecUrl": "https://api.openai.com/openapi.json" + }, + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "resourceMappings": { + "resources": [ + { + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } + } + ] + }, + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM provider template updated successfully|[LLMProviderTemplate](schemas.md#schemallmprovidertemplate)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Enable or disable a template version + + + +`PATCH /llm-provider-templates/{llmProviderTemplateId}` + +> Code samples + +```shell + +curl -X PATCH https://localhost:9243/api/v0.9/llm-provider-templates/{llmProviderTemplateId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Enable or disable the single template version identified by its handle +(id). Only built-in templates can be toggled; disabling a version in use +by a provider is rejected (409). + +> Payload + +```json +{ + "enabled": false +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderTemplateId|path|string|true|Unique handle of the template version.| +|body|body|object|true|none| +|» enabled|body|boolean|true|none| + +> Example responses + +> 200 Response + +```json +{ + "id": "openai", + "groupId": "openai", + "displayName": "OpenAI", + "managedBy": "wso2", + "description": "Default OpenAI template", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "readOnly": false, + "version": "v1.0", + "isLatest": true, + "enabled": true, + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "metadata": { + "endpointUrl": "https://api.openai.com", + "auth": { + "type": "bearer", + "header": "Authorization", + "valuePrefix": "Bearer " + }, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "openapiSpecUrl": "https://api.openai.com/openapi.json" + }, + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "resourceMappings": { + "resources": [ + { + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } + } + ] + }, + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Updated template version|[LLMProviderTemplate](schemas.md#schemallmprovidertemplate)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete a template version + + + +`DELETE /llm-provider-templates/{llmProviderTemplateId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/llm-provider-templates/{llmProviderTemplateId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Delete the single template version identified by its handle (id). +Built-in versions are read-only (403); a version still in use by a +provider is blocked (409). + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderTemplateId|path|string|true|Unique handle of the template version.| + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Version deleted successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/llm-providers.md b/docs/rest-apis/platform-api/llm-providers.md new file mode 100644 index 0000000000..c24fdd07e4 --- /dev/null +++ b/docs/rest-apis/platform-api/llm-providers.md @@ -0,0 +1,2012 @@ +

LLM Providers

+ +LLM provider management operations + +## Create a new LLM provider + + + +`POST /llm-providers` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/llm-providers \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Deploy a new LLM provider configuration. + +> Payload + +```json +{ + "id": "wso2-openai-provider", + "displayName": "WSO2 OpenAI Provider", + "description": "Primary OpenAI provider", + "version": "v1.0", + "context": "/openai", + "vhost": "api.openai", + "template": "openai", + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "modelProviders": [ + { + "id": "claude", + "displayName": "Claude", + "models": [ + { + "id": "claude-3-5-sonnet", + "displayName": "Claude 3.5 Sonnet", + "description": "High-quality reasoning model" + } + ] + } + ], + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ] + } + ] + }, + "rateLimiting": { + "providerLevel": { + "global": { + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + "consumerLevel": { + "resourceWise": { + "default": { + "request": { + "enabled": true, + "count": 50, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 100, + "reset": { + "duration": 1, + "unit": "month" + } + } + }, + "resources": [ + { + "resource": "/models", + "limit": { + "request": { + "enabled": true, + "count": 200, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 100000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + { + "resource": "/chat/completions", + "limit": { + "request": { + "enabled": true, + "count": 25, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 10, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + } + ] + } + } + }, + "globalPolicies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operationPolicies": [ + { + "name": "token-based-ratelimit", + "version": "v1", + "executionCondition": "string", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "policies": [ + { + "name": "budgetControl", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "security": { + "enabled": true, + "apiKey": { + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" + } + }, + "associatedGateways": [ + { + "id": "prod-eu", + "configurations": { + "host": "prod-eu.platform-gw.local" + } + }, + { + "id": "prod-us", + "configurations": { + "host": "prod-us.platform-gw.local" + } + } + ] +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[LLMProvider](schemas.md#schemallmprovider)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "id": "wso2-openai-provider", + "displayName": "WSO2 OpenAI Provider", + "description": "Primary OpenAI provider", + "createdBy": "john.doe", + "readOnly": false, + "updatedBy": "john.doe", + "version": "v1.0", + "context": "/openai", + "vhost": "api.openai", + "template": "openai", + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "modelProviders": [ + { + "id": "claude", + "displayName": "Claude", + "models": [ + { + "id": "claude-3-5-sonnet", + "displayName": "Claude 3.5 Sonnet", + "description": "High-quality reasoning model" + } + ] + } + ], + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ] + } + ] + }, + "rateLimiting": { + "providerLevel": { + "global": { + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + "consumerLevel": { + "resourceWise": { + "default": { + "request": { + "enabled": true, + "count": 50, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 100, + "reset": { + "duration": 1, + "unit": "month" + } + } + }, + "resources": [ + { + "resource": "/models", + "limit": { + "request": { + "enabled": true, + "count": 200, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 100000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + { + "resource": "/chat/completions", + "limit": { + "request": { + "enabled": true, + "count": 25, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 10, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + } + ] + } + } + }, + "globalPolicies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operationPolicies": [ + { + "name": "token-based-ratelimit", + "version": "v1", + "executionCondition": "string", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "policies": [ + { + "name": "budgetControl", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "security": { + "enabled": true, + "apiKey": { + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" + } + }, + "associatedGateways": [ + { + "id": "prod-eu", + "configurations": { + "host": "prod-eu.platform-gw.local" + } + }, + { + "id": "prod-us", + "configurations": { + "host": "prod-us.platform-gw.local" + } + } + ], + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|LLM provider created successfully|[LLMProvider](schemas.md#schemallmprovider)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## List all LLM providers + + + +`GET /llm-providers` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/llm-providers \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieve a list of all LLM providers. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "id": "wso2-openai-provider", + "displayName": "WSO2 OpenAI Provider", + "description": "Primary OpenAI provider", + "createdBy": "john.doe", + "readOnly": false, + "version": "v1.0", + "template": "openai", + "status": "deployed", + "createdAt": "2025-11-25T10:30:00Z", + "updatedAt": "2025-11-25T10:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of LLM providers|[LLMProviderListResponse](schemas.md#schemallmproviderlistresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get LLM provider by identifier + + + +`GET /llm-providers/{llmProviderId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/llm-providers/{llmProviderId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieve the complete configuration for a specific LLM provider. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderId|path|string|true|Unique identifier of the LLM provider| + +> Example responses + +> 200 Response + +```json +{ + "id": "wso2-openai-provider", + "displayName": "WSO2 OpenAI Provider", + "description": "Primary OpenAI provider", + "createdBy": "john.doe", + "readOnly": false, + "updatedBy": "john.doe", + "version": "v1.0", + "context": "/openai", + "vhost": "api.openai", + "template": "openai", + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "modelProviders": [ + { + "id": "claude", + "displayName": "Claude", + "models": [ + { + "id": "claude-3-5-sonnet", + "displayName": "Claude 3.5 Sonnet", + "description": "High-quality reasoning model" + } + ] + } + ], + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ] + } + ] + }, + "rateLimiting": { + "providerLevel": { + "global": { + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + "consumerLevel": { + "resourceWise": { + "default": { + "request": { + "enabled": true, + "count": 50, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 100, + "reset": { + "duration": 1, + "unit": "month" + } + } + }, + "resources": [ + { + "resource": "/models", + "limit": { + "request": { + "enabled": true, + "count": 200, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 100000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + { + "resource": "/chat/completions", + "limit": { + "request": { + "enabled": true, + "count": 25, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 10, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + } + ] + } + } + }, + "globalPolicies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operationPolicies": [ + { + "name": "token-based-ratelimit", + "version": "v1", + "executionCondition": "string", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "policies": [ + { + "name": "budgetControl", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "security": { + "enabled": true, + "apiKey": { + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" + } + }, + "associatedGateways": [ + { + "id": "prod-eu", + "configurations": { + "host": "prod-eu.platform-gw.local" + } + }, + { + "id": "prod-us", + "configurations": { + "host": "prod-us.platform-gw.local" + } + } + ], + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM provider details|[LLMProvider](schemas.md#schemallmprovider)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Update an existing LLM provider + + + +`PUT /llm-providers/{llmProviderId}` + +> Code samples + +```shell + +curl -X PUT https://localhost:9243/api/v0.9/llm-providers/{llmProviderId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update the configuration of an existing LLM provider. + +> Payload + +```json +{ + "id": "wso2-openai-provider", + "displayName": "WSO2 OpenAI Provider", + "description": "Primary OpenAI provider", + "version": "v1.0", + "context": "/openai", + "vhost": "api.openai", + "template": "openai", + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "modelProviders": [ + { + "id": "claude", + "displayName": "Claude", + "models": [ + { + "id": "claude-3-5-sonnet", + "displayName": "Claude 3.5 Sonnet", + "description": "High-quality reasoning model" + } + ] + } + ], + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ] + } + ] + }, + "rateLimiting": { + "providerLevel": { + "global": { + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + "consumerLevel": { + "resourceWise": { + "default": { + "request": { + "enabled": true, + "count": 50, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 100, + "reset": { + "duration": 1, + "unit": "month" + } + } + }, + "resources": [ + { + "resource": "/models", + "limit": { + "request": { + "enabled": true, + "count": 200, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 100000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + { + "resource": "/chat/completions", + "limit": { + "request": { + "enabled": true, + "count": 25, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 10, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + } + ] + } + } + }, + "globalPolicies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operationPolicies": [ + { + "name": "token-based-ratelimit", + "version": "v1", + "executionCondition": "string", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "policies": [ + { + "name": "budgetControl", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "security": { + "enabled": true, + "apiKey": { + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" + } + }, + "associatedGateways": [ + { + "id": "prod-eu", + "configurations": { + "host": "prod-eu.platform-gw.local" + } + }, + { + "id": "prod-us", + "configurations": { + "host": "prod-us.platform-gw.local" + } + } + ] +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderId|path|string|true|Unique identifier of the LLM provider| +|body|body|[LLMProvider](schemas.md#schemallmprovider)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "id": "wso2-openai-provider", + "displayName": "WSO2 OpenAI Provider", + "description": "Primary OpenAI provider", + "createdBy": "john.doe", + "readOnly": false, + "updatedBy": "john.doe", + "version": "v1.0", + "context": "/openai", + "vhost": "api.openai", + "template": "openai", + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "modelProviders": [ + { + "id": "claude", + "displayName": "Claude", + "models": [ + { + "id": "claude-3-5-sonnet", + "displayName": "Claude 3.5 Sonnet", + "description": "High-quality reasoning model" + } + ] + } + ], + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ] + } + ] + }, + "rateLimiting": { + "providerLevel": { + "global": { + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + "consumerLevel": { + "resourceWise": { + "default": { + "request": { + "enabled": true, + "count": 50, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 100, + "reset": { + "duration": 1, + "unit": "month" + } + } + }, + "resources": [ + { + "resource": "/models", + "limit": { + "request": { + "enabled": true, + "count": 200, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 100000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + { + "resource": "/chat/completions", + "limit": { + "request": { + "enabled": true, + "count": 25, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 10, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + } + ] + } + } + }, + "globalPolicies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operationPolicies": [ + { + "name": "token-based-ratelimit", + "version": "v1", + "executionCondition": "string", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "policies": [ + { + "name": "budgetControl", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "security": { + "enabled": true, + "apiKey": { + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" + } + }, + "associatedGateways": [ + { + "id": "prod-eu", + "configurations": { + "host": "prod-eu.platform-gw.local" + } + }, + { + "id": "prod-us", + "configurations": { + "host": "prod-us.platform-gw.local" + } + } + ], + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM provider updated successfully|[LLMProvider](schemas.md#schemallmprovider)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete an LLM provider + + + +`DELETE /llm-providers/{llmProviderId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/llm-providers/{llmProviderId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Remove an LLM provider. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderId|path|string|true|Unique identifier of the LLM provider| + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|LLM provider deleted successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## List LLM proxies by provider + + + +`GET /llm-providers/{llmProviderId}/llm-proxies` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/llm-providers/{llmProviderId}/llm-proxies \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieve a list of LLM proxies that use the specified LLM provider. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderId|path|string|true|Unique identifier of the LLM provider| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "id": "wso2-con-assistant", + "displayName": "WSO2 Con Assistant", + "description": "Customer support assistant", + "createdBy": "john.doe", + "context": "/wso2-con-assistant", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "provider": "wso2-openai-provider", + "status": "deployed", + "createdAt": "2025-11-25T10:30:00Z", + "updatedAt": "2025-11-25T10:30:00Z", + "readOnly": false + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of LLM proxies|[LLMProxyListResponse](schemas.md#schemallmproxylistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Create a new API key for an LLM provider + + + +`POST /llm-providers/{llmProviderId}/api-keys` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/llm-providers/{llmProviderId}/api-keys \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Generates a new API key for the specified LLM provider. The generated key +is broadcasted to all gateways in the organization and can be used to +authenticate requests to the LLM provider when API key validation is enabled. + +> Payload + +```json +{ + "id": "production-key", + "displayName": "Production Key", + "expiresAt": "2026-12-31T23:59:59Z", + "issuer": "api-platform-devportal", + "allowedTargets": "dev_gateway,test_gateway" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderId|path|string|true|Unique identifier of the LLM provider| +|body|body|[CreateLLMProviderAPIKeyRequest](schemas.md#schemacreatellmproviderapikeyrequest)|true|API key creation details| + +> Example responses + +> 201 Response + +```json +{ + "status": "success", + "message": "API key created and broadcasted to gateways successfully", + "id": "production-key", + "apiKey": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +> 503 Response + +```json +{ + "status": "error", + "code": "GATEWAY_CONNECTION_UNAVAILABLE", + "message": "No gateway connections are currently available.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|API key created successfully|[CreateLLMProviderAPIKeyResponse](schemas.md#schemacreatellmproviderapikeyresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|Service Unavailable. No gateway connections are currently available to service this request.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## List API keys for an LLM provider + + + +`GET /llm-providers/{llmProviderId}/api-keys` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/llm-providers/{llmProviderId}/api-keys \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Returns all API keys associated with the specified LLM provider. The plain key value is never returned. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderId|path|string|true|Unique identifier of the LLM provider| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +> Example responses + +> 200 Response + +```json +{ + "list": [ + { + "id": "string", + "displayName": "string", + "maskedApiKey": "string", + "status": "active", + "createdAt": "2019-08-24T14:15:22Z", + "createdBy": "john.doe", + "updatedAt": "2019-08-24T14:15:22Z", + "expiresAt": "2019-08-24T14:15:22Z", + "issuer": "api-platform-devportal", + "allowedTargets": "string" + } + ], + "count": 0, + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of API keys retrieved successfully|[LLMProviderAPIKeyListResponse](schemas.md#schemallmproviderapikeylistresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete an API key for an LLM provider + + + +`DELETE /llm-providers/{llmProviderId}/api-keys/{apiKeyId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/llm-providers/{llmProviderId}/api-keys/{apiKeyId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Deletes the key from the database and broadcasts a revoke event to the allowed gateways. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProviderId|path|string|true|Unique identifier of the LLM provider| +|apiKeyId|path|string|true|Name of the API key to delete| + +> Example responses + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +> 503 Response + +```json +{ + "status": "error", + "code": "GATEWAY_CONNECTION_UNAVAILABLE", + "message": "No gateway connections are currently available.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|API key deleted successfully|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|Service Unavailable. No gateway connections are currently available to service this request.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/llm-proxies.md b/docs/rest-apis/platform-api/llm-proxies.md new file mode 100644 index 0000000000..c930f1b269 --- /dev/null +++ b/docs/rest-apis/platform-api/llm-proxies.md @@ -0,0 +1,1315 @@ +

LLM Proxies

+ +LLM proxy management operations + +## Create a new LLM proxy + + + +`POST /llm-proxies` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/llm-proxies \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Deploy a new LLM proxy configuration. + +> Payload + +```json +{ + "id": "wso2-con-assistant", + "displayName": "WSO2 Con Assistant", + "description": "Customer support assistant", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "context": "/openai", + "vhost": "api.openai", + "provider": { + "id": "wso2-openai-provider", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "additionalProviders": [ + { + "id": "anthropic-provider", + "as": "anthropic-upstream", + "transformer": { + "type": "openai-to-anthropic", + "version": "v1", + "params": {} + } + } + ], + "openapi": "openapi: 3.0.3\ninfo:\n title: Proxy API\n version: v1.0\npaths: {}\n", + "globalPolicies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operationPolicies": [ + { + "name": "token-based-ratelimit", + "version": "v1", + "executionCondition": "string", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "policies": [ + { + "name": "budgetControl", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "security": { + "enabled": true, + "apiKey": { + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" + } + }, + "associatedGateways": [ + { + "id": "prod-eu" + }, + { + "id": "prod-us" + } + ] +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[LLMProxy](schemas.md#schemallmproxy)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "id": "wso2-con-assistant", + "displayName": "WSO2 Con Assistant", + "description": "Customer support assistant", + "createdBy": "john.doe", + "readOnly": false, + "updatedBy": "john.doe", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "context": "/openai", + "vhost": "api.openai", + "provider": { + "id": "wso2-openai-provider", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + }, + "additionalProviders": [ + { + "id": "anthropic-provider", + "as": "anthropic-upstream", + "transformer": { + "type": "openai-to-anthropic", + "version": "v1", + "params": {} + } + } + ], + "openapi": "openapi: 3.0.3\ninfo:\n title: Proxy API\n version: v1.0\npaths: {}\n", + "globalPolicies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operationPolicies": [ + { + "name": "token-based-ratelimit", + "version": "v1", + "executionCondition": "string", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "policies": [ + { + "name": "budgetControl", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "security": { + "enabled": true, + "apiKey": { + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" + } + }, + "associatedGateways": [ + { + "id": "prod-eu" + }, + { + "id": "prod-us" + } + ], + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|LLM proxy created successfully|[LLMProxy](schemas.md#schemallmproxy)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## List all LLM proxies + + + +`GET /llm-proxies` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/llm-proxies?projectId=default-project \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieve a list of all LLM proxies for a project. Requires the projectId query parameter. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|projectId|query|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by.| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +#### Detailed descriptions + +**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by. + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "id": "wso2-con-assistant", + "displayName": "WSO2 Con Assistant", + "description": "Customer support assistant", + "createdBy": "john.doe", + "context": "/wso2-con-assistant", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "provider": "wso2-openai-provider", + "status": "deployed", + "createdAt": "2025-11-25T10:30:00Z", + "updatedAt": "2025-11-25T10:30:00Z", + "readOnly": false + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of LLM proxies|[LLMProxyListResponse](schemas.md#schemallmproxylistresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get LLM proxy by unique identifier + + + +`GET /llm-proxies/{llmProxyId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/llm-proxies/{llmProxyId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieve the complete configuration for a specific LLM proxy. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProxyId|path|string|true|Unique identifier of the LLM proxy| + +> Example responses + +> 200 Response + +```json +{ + "id": "wso2-con-assistant", + "displayName": "WSO2 Con Assistant", + "description": "Customer support assistant", + "createdBy": "john.doe", + "readOnly": false, + "updatedBy": "john.doe", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "context": "/openai", + "vhost": "api.openai", + "provider": { + "id": "wso2-openai-provider", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + }, + "additionalProviders": [ + { + "id": "anthropic-provider", + "as": "anthropic-upstream", + "transformer": { + "type": "openai-to-anthropic", + "version": "v1", + "params": {} + } + } + ], + "openapi": "openapi: 3.0.3\ninfo:\n title: Proxy API\n version: v1.0\npaths: {}\n", + "globalPolicies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operationPolicies": [ + { + "name": "token-based-ratelimit", + "version": "v1", + "executionCondition": "string", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "policies": [ + { + "name": "budgetControl", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "security": { + "enabled": true, + "apiKey": { + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" + } + }, + "associatedGateways": [ + { + "id": "prod-eu" + }, + { + "id": "prod-us" + } + ], + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM proxy details|[LLMProxy](schemas.md#schemallmproxy)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Update an existing LLM proxy + + + +`PUT /llm-proxies/{llmProxyId}` + +> Code samples + +```shell + +curl -X PUT https://localhost:9243/api/v0.9/llm-proxies/{llmProxyId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update the configuration of an existing LLM proxy. + +> Payload + +```json +{ + "id": "wso2-con-assistant", + "displayName": "WSO2 Con Assistant", + "description": "Customer support assistant", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "context": "/openai", + "vhost": "api.openai", + "provider": { + "id": "wso2-openai-provider", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "additionalProviders": [ + { + "id": "anthropic-provider", + "as": "anthropic-upstream", + "transformer": { + "type": "openai-to-anthropic", + "version": "v1", + "params": {} + } + } + ], + "openapi": "openapi: 3.0.3\ninfo:\n title: Proxy API\n version: v1.0\npaths: {}\n", + "globalPolicies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operationPolicies": [ + { + "name": "token-based-ratelimit", + "version": "v1", + "executionCondition": "string", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "policies": [ + { + "name": "budgetControl", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "security": { + "enabled": true, + "apiKey": { + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" + } + }, + "associatedGateways": [ + { + "id": "prod-eu" + }, + { + "id": "prod-us" + } + ] +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProxyId|path|string|true|Unique identifier of the LLM proxy| +|body|body|[LLMProxy](schemas.md#schemallmproxy)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "id": "wso2-con-assistant", + "displayName": "WSO2 Con Assistant", + "description": "Customer support assistant", + "createdBy": "john.doe", + "readOnly": false, + "updatedBy": "john.doe", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "context": "/openai", + "vhost": "api.openai", + "provider": { + "id": "wso2-openai-provider", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + }, + "additionalProviders": [ + { + "id": "anthropic-provider", + "as": "anthropic-upstream", + "transformer": { + "type": "openai-to-anthropic", + "version": "v1", + "params": {} + } + } + ], + "openapi": "openapi: 3.0.3\ninfo:\n title: Proxy API\n version: v1.0\npaths: {}\n", + "globalPolicies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operationPolicies": [ + { + "name": "token-based-ratelimit", + "version": "v1", + "executionCondition": "string", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "policies": [ + { + "name": "budgetControl", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "security": { + "enabled": true, + "apiKey": { + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" + } + }, + "associatedGateways": [ + { + "id": "prod-eu" + }, + { + "id": "prod-us" + } + ], + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM proxy updated successfully|[LLMProxy](schemas.md#schemallmproxy)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete an LLM proxy + + + +`DELETE /llm-proxies/{llmProxyId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/llm-proxies/{llmProxyId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Remove an LLM proxy. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProxyId|path|string|true|Unique identifier of the LLM proxy| + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|LLM proxy deleted successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Create a new API key for an LLM proxy + + + +`POST /llm-proxies/{llmProxyId}/api-keys` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/llm-proxies/{llmProxyId}/api-keys \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Generates a new API key for the specified LLM proxy. The generated key +is broadcasted to all gateways in the organization and can be used to +authenticate requests to the LLM proxy when API key validation is enabled. + +> Payload + +```json +{ + "id": "production-key", + "displayName": "Production Key", + "expiresAt": "2026-12-31T23:59:59Z", + "issuer": "api-platform-devportal", + "allowedTargets": "dev_gateway,test_gateway" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProxyId|path|string|true|Unique identifier of the LLM proxy| +|body|body|[CreateLLMProxyAPIKeyRequest](schemas.md#schemacreatellmproxyapikeyrequest)|true|API key creation details| + +> Example responses + +> 201 Response + +```json +{ + "status": "success", + "message": "API key created and broadcasted to gateways successfully", + "id": "production-key", + "apiKey": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +> 503 Response + +```json +{ + "status": "error", + "code": "GATEWAY_CONNECTION_UNAVAILABLE", + "message": "No gateway connections are currently available.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|API key created successfully|[CreateLLMProxyAPIKeyResponse](schemas.md#schemacreatellmproxyapikeyresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|Service Unavailable. No gateway connections are currently available to service this request.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## List API keys for an LLM proxy + + + +`GET /llm-proxies/{llmProxyId}/api-keys` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/llm-proxies/{llmProxyId}/api-keys \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Returns all API keys associated with the specified LLM proxy. The plain key value is never returned. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProxyId|path|string|true|Unique identifier of the LLM proxy| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +> Example responses + +> 200 Response + +```json +{ + "list": [ + { + "id": "string", + "displayName": "string", + "maskedApiKey": "string", + "status": "active", + "createdAt": "2019-08-24T14:15:22Z", + "createdBy": "john.doe", + "updatedAt": "2019-08-24T14:15:22Z", + "expiresAt": "2019-08-24T14:15:22Z", + "issuer": "api-platform-devportal", + "allowedTargets": "string" + } + ], + "count": 0, + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of API keys retrieved successfully|[LLMProxyAPIKeyListResponse](schemas.md#schemallmproxyapikeylistresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete an API key for an LLM proxy + + + +`DELETE /llm-proxies/{llmProxyId}/api-keys/{apiKeyId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/llm-proxies/{llmProxyId}/api-keys/{apiKeyId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Deletes the key from the database and broadcasts a revoke event to the allowed gateways. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProxyId|path|string|true|Unique identifier of the LLM proxy| +|apiKeyId|path|string|true|Name of the API key to delete| + +> Example responses + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +> 503 Response + +```json +{ + "status": "error", + "code": "GATEWAY_CONNECTION_UNAVAILABLE", + "message": "No gateway connections are currently available.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|API key deleted successfully|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|Service Unavailable. No gateway connections are currently available to service this request.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/llm-proxy-deployments.md b/docs/rest-apis/platform-api/llm-proxy-deployments.md new file mode 100644 index 0000000000..fed947c912 --- /dev/null +++ b/docs/rest-apis/platform-api/llm-proxy-deployments.md @@ -0,0 +1,767 @@ +

LLM Proxy Deployments

+ +LLM proxy deployment operations + +## Create and deploy a new LLM proxy deployment + + + +`POST /llm-proxies/{llmProxyId}/deployments` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/llm-proxies/{llmProxyId}/deployments \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Creates an immutable deployment artifact for an LLM proxy and deploys it to a specified gateway. +Each deployment targets a single gateway. The proxyId parameter is the LLM proxy handle (identifier), +not the UUID. The operation returns a transitional DEPLOYING status. Final success or failure will be reported asynchronously via the deployment's status and statusReason once the gateway acknowledges. +Access is validated against the organization in the JWT token. + +> Payload + +```json +{ + "name": "v1.0-production", + "base": "current", + "gatewayId": "prod-gateway-01", + "metadata": {} +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProxyId|path|string|true|Unique identifier of the LLM proxy| +|body|body|[DeployRequest](schemas.md#schemadeployrequest)|true|Deployment request with gateway ID, base reference, and metadata| + +> Example responses + +> 201 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|LLM proxy deployed successfully|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## Get deployments for an LLM proxy + + + +`GET /llm-proxies/{llmProxyId}/deployments` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/llm-proxies/{llmProxyId}/deployments \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves all deployment artifacts for a specific LLM proxy. The proxyId parameter is the +LLM proxy handle (identifier), not the UUID. Supports filtering by gateway UUID and deployment status. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProxyId|path|string|true|Unique identifier of the LLM proxy| +|gatewayId|query|string|false|**Gateway ID** consisting of the **UUID** of the Gateway to filter status by.| +|status|query|string|false|Filter deployments by status (DEPLOYED, UNDEPLOYED, DEPLOYING, UNDEPLOYING, FAILED, or ARCHIVED)| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +#### Detailed descriptions + +**gatewayId**: **Gateway ID** consisting of the **UUID** of the Gateway to filter status by. + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|status|DEPLOYED| +|status|UNDEPLOYED| +|status|DEPLOYING| +|status|UNDEPLOYING| +|status|FAILED| +|status|ARCHIVED| + +> Example responses + +> 200 Response + +```json +{ + "count": 0, + "list": [ + { + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Deployments retrieved successfully|[DeploymentListResponse](schemas.md#schemadeploymentlistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get LLM proxy deployment by ID + + + +`GET /llm-proxies/{llmProxyId}/deployments/{deploymentId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/llm-proxies/{llmProxyId}/deployments/{deploymentId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves metadata for a specific LLM proxy deployment artifact including status, gateway association, +and timestamps. Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProxyId|path|string|true|Unique identifier of the LLM proxy| +|deploymentId|path|string(uuid)|true|The UUID of the deployment| + +> Example responses + +> 200 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Deployment metadata retrieved successfully|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete LLM proxy deployment + + + +`DELETE /llm-proxies/{llmProxyId}/deployments/{deploymentId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/llm-proxies/{llmProxyId}/deployments/{deploymentId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Deletes a deployment artifact. Deletion is only allowed when the deployment is in UNDEPLOYED status. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProxyId|path|string|true|Unique identifier of the LLM proxy| +|deploymentId|path|string(uuid)|true|The UUID of the deployment| + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Deployment deleted successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Undeploy LLM proxy deployment from gateway + + + +`POST /llm-proxies/{llmProxyId}/deployments/{deploymentId}/undeploy` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/llm-proxies/{llmProxyId}/deployments/{deploymentId}/undeploy?gatewayId=string \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Undeploys an active LLM proxy deployment, stopping it from being served on the specified gateway. +The deployment artifact remains in the system and can be restored later. +Returns the updated deployment object with initial status UNDEPLOYING. Final status (UNDEPLOYED or FAILED) will be reported asynchronously via the deployment's status and statusReason once the gateway acknowledges. + +The gatewayId query parameter is validated against the deployment's bound gateway to prevent unintended operations. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProxyId|path|string|true|Unique identifier of the LLM proxy| +|deploymentId|path|string|true|UUID of the deployment to undeploy| +|gatewayId|query|string|true|UUID of the gateway (validated against deployment's bound gateway)| + +> Example responses + +> 200 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Undeploy initiated successfully. Returns the deployment with initial status UNDEPLOYING. Poll status for final result.|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Restore a previous LLM proxy deployment + + + +`POST /llm-proxies/{llmProxyId}/deployments/{deploymentId}/restore` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/llm-proxies/{llmProxyId}/deployments/{deploymentId}/restore?gatewayId=string \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Initiates restoring a previous deployment (ARCHIVED or UNDEPLOYED) on the specified gateway. +Returns the deployment with initial status DEPLOYING. Final success or failure will be reported asynchronously via the deployment's status and statusReason once the gateway acknowledges. +The target deployment must not already be in DEPLOYED status. + +The gatewayId query parameter is validated against the deployment's bound gateway to prevent unintended operations. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|llmProxyId|path|string|true|Unique identifier of the LLM proxy| +|deploymentId|path|string|true|UUID of the deployment to restore (must be ARCHIVED or UNDEPLOYED)| +|gatewayId|query|string|true|UUID of the gateway (validated against deployment's bound gateway)| + +> Example responses + +> 200 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Restore initiated successfully. Returns the deployment with initial status DEPLOYING. Poll status for final result.|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/mcp-proxies.md b/docs/rest-apis/platform-api/mcp-proxies.md new file mode 100644 index 0000000000..446e20db60 --- /dev/null +++ b/docs/rest-apis/platform-api/mcp-proxies.md @@ -0,0 +1,962 @@ +

MCP Proxies

+ +MCP proxy management operations + +## Create a new MCP proxy + + + +`POST /mcp-proxies` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/mcp-proxies \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Deploy a new MCP proxy configuration. + +> Payload + +```json +{ + "id": "weather-mcp-proxy", + "displayName": "Weather Server", + "description": "An MCP server which provides weather information", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "context": "/", + "vhost": "mcp.gw.com", + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + } + }, + "mcpSpecVersion": "2025-06-18", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "kind": "Mcp", + "capabilities": { + "tools": [ + {} + ], + "resources": [ + {} + ], + "prompts": [ + {} + ] + }, + "associatedGateways": [ + { + "id": "prod-eu" + }, + { + "id": "prod-us" + } + ] +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[MCPProxy](schemas.md#schemamcpproxy)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "id": "weather-mcp-proxy", + "displayName": "Weather Server", + "description": "An MCP server which provides weather information", + "createdBy": "john.doe", + "readOnly": false, + "updatedBy": "john.doe", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "context": "/", + "vhost": "mcp.gw.com", + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + } + }, + "mcpSpecVersion": "2025-06-18", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "kind": "Mcp", + "capabilities": { + "tools": [ + {} + ], + "resources": [ + {} + ], + "prompts": [ + {} + ] + }, + "associatedGateways": [ + { + "id": "prod-eu" + }, + { + "id": "prod-us" + } + ], + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|MCP proxy created successfully|[MCPProxy](schemas.md#schemamcpproxy)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## List all MCP proxies + + + +`GET /mcp-proxies` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/mcp-proxies?projectId=default-project \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieve a list of all MCP proxies for a project. Requires the projectId query parameter. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|projectId|query|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by.| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +#### Detailed descriptions + +**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by. + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "id": "weather-mcp-proxy", + "displayName": "Weather Server", + "description": "An MCP server which provides weather information", + "createdBy": "john.doe", + "context": "/weather-mcp-proxy", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "status": "deployed", + "mcpSpecVersion": "2025-11-25", + "createdAt": "2025-11-25T10:30:00Z", + "updatedAt": "2025-11-25T10:30:00Z", + "readOnly": false + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of MCP proxies|[MCPProxyListResponse](schemas.md#schemamcpproxylistresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get MCP proxy by unique identifier + + + +`GET /mcp-proxies/{mcpProxyId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/mcp-proxies/{mcpProxyId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieve the complete configuration for a specific MCP proxy. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|mcpProxyId|path|string|true|Unique identifier of the MCP proxy| + +> Example responses + +> 200 Response + +```json +{ + "id": "weather-mcp-proxy", + "displayName": "Weather Server", + "description": "An MCP server which provides weather information", + "createdBy": "john.doe", + "readOnly": false, + "updatedBy": "john.doe", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "context": "/", + "vhost": "mcp.gw.com", + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + } + }, + "mcpSpecVersion": "2025-06-18", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "kind": "Mcp", + "capabilities": { + "tools": [ + {} + ], + "resources": [ + {} + ], + "prompts": [ + {} + ] + }, + "associatedGateways": [ + { + "id": "prod-eu" + }, + { + "id": "prod-us" + } + ], + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|MCP proxy details|[MCPProxy](schemas.md#schemamcpproxy)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Update an existing MCP proxy + + + +`PUT /mcp-proxies/{mcpProxyId}` + +> Code samples + +```shell + +curl -X PUT https://localhost:9243/api/v0.9/mcp-proxies/{mcpProxyId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update the configuration of an existing MCP proxy. + +> Payload + +```json +{ + "id": "weather-mcp-proxy", + "displayName": "Weather Server", + "description": "An MCP server which provides weather information", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "context": "/", + "vhost": "mcp.gw.com", + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + } + }, + "mcpSpecVersion": "2025-06-18", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "kind": "Mcp", + "capabilities": { + "tools": [ + {} + ], + "resources": [ + {} + ], + "prompts": [ + {} + ] + }, + "associatedGateways": [ + { + "id": "prod-eu" + }, + { + "id": "prod-us" + } + ] +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|mcpProxyId|path|string|true|Unique identifier of the MCP proxy| +|body|body|[MCPProxy](schemas.md#schemamcpproxy)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "id": "weather-mcp-proxy", + "displayName": "Weather Server", + "description": "An MCP server which provides weather information", + "createdBy": "john.doe", + "readOnly": false, + "updatedBy": "john.doe", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "context": "/", + "vhost": "mcp.gw.com", + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + } + }, + "mcpSpecVersion": "2025-06-18", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "kind": "Mcp", + "capabilities": { + "tools": [ + {} + ], + "resources": [ + {} + ], + "prompts": [ + {} + ] + }, + "associatedGateways": [ + { + "id": "prod-eu" + }, + { + "id": "prod-us" + } + ], + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|MCP proxy updated successfully|[MCPProxy](schemas.md#schemamcpproxy)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete an MCP proxy + + + +`DELETE /mcp-proxies/{mcpProxyId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/mcp-proxies/{mcpProxyId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Remove an MCP proxy. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|mcpProxyId|path|string|true|Unique identifier of the MCP proxy| + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|MCP proxy deleted successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Fetch server info from MCP proxy backend services + + + +`POST /mcp-proxies/fetch-server-info` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/mcp-proxies/fetch-server-info \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Fetches server information from the backend services of an MCP proxy. +This is used to validate connectivity and retrieve metadata about the backend services. + +> Payload + +```json +{ + "url": "https://mcp.server.com/mcp", + "proxyId": "my-mcp-proxy", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[MCPServerInfoFetchRequest](schemas.md#schemamcpserverinfofetchrequest)|true|Deployment request with gateway ID, base reference, and metadata| + +> Example responses + +> 200 Response + +```json +{ + "serverInfo": {}, + "tools": [ + {} + ], + "resources": [ + {} + ], + "prompts": [ + {} + ] +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Server info retrieved successfully|[MCPServerInfoFetchResponse](schemas.md#schemamcpserverinfofetchresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/mcp-proxy-deployments.md b/docs/rest-apis/platform-api/mcp-proxy-deployments.md new file mode 100644 index 0000000000..5c6dab0934 --- /dev/null +++ b/docs/rest-apis/platform-api/mcp-proxy-deployments.md @@ -0,0 +1,723 @@ +

MCP Proxy Deployments

+ +MCP proxy deployment operations + +## Create and deploy a new deployment for MCP proxy + + + +`POST /mcp-proxies/{mcpProxyId}/deployments` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/mcp-proxies/{mcpProxyId}/deployments \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Creates an immutable deployment artifact for an MCP proxy and deploys it to a specified gateway. +Each deployment targets a single gateway. The id parameter is the MCP proxy handle (identifier), +not the UUID. The operation returns a transitional DEPLOYING status. Final success or failure will be reported asynchronously via the deployment's status and statusReason once the gateway acknowledges. +Access is validated against the organization in the JWT token. + +> Payload + +```json +{ + "name": "v1.0-production", + "base": "current", + "gatewayId": "prod-gateway-01", + "metadata": {} +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|mcpProxyId|path|string|true|Unique identifier of the MCP proxy| +|body|body|[DeployRequest](schemas.md#schemadeployrequest)|true|Deployment request with gateway ID, base reference, and metadata| + +> Example responses + +> 201 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|MCP proxy deployed successfully|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## Get deployments for an MCP proxy + + + +`GET /mcp-proxies/{mcpProxyId}/deployments` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/mcp-proxies/{mcpProxyId}/deployments \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves all deployment artifacts for a specific MCP proxy. The id parameter is the MCP proxy handle (identifier), +not the UUID. Supports filtering by gateway UUID and deployment status. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|mcpProxyId|path|string|true|Unique identifier of the MCP proxy| +|gatewayId|query|string|false|**Gateway ID** consisting of the **UUID** of the Gateway to filter status by.| +|status|query|string|false|Filter deployments by status (DEPLOYED, UNDEPLOYED, DEPLOYING, UNDEPLOYING, FAILED, or ARCHIVED)| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +#### Detailed descriptions + +**gatewayId**: **Gateway ID** consisting of the **UUID** of the Gateway to filter status by. + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|status|DEPLOYED| +|status|UNDEPLOYED| +|status|DEPLOYING| +|status|UNDEPLOYING| +|status|FAILED| +|status|ARCHIVED| + +> Example responses + +> 200 Response + +```json +{ + "count": 0, + "list": [ + { + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Deployments retrieved successfully|[DeploymentListResponse](schemas.md#schemadeploymentlistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get deployment by ID + + + +`GET /mcp-proxies/{mcpProxyId}/deployments/{deploymentId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/mcp-proxies/{mcpProxyId}/deployments/{deploymentId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves metadata for a specific deployment artifact including status, gateway association, +and timestamps. Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|mcpProxyId|path|string|true|Unique identifier of the MCP proxy| +|deploymentId|path|string(uuid)|true|The UUID of the deployment| + +> Example responses + +> 200 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Deployment metadata retrieved successfully|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete deployment + + + +`DELETE /mcp-proxies/{mcpProxyId}/deployments/{deploymentId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/mcp-proxies/{mcpProxyId}/deployments/{deploymentId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Deletes a deployment artifact. Deletion is only allowed when the deployment is in UNDEPLOYED status. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|mcpProxyId|path|string|true|Unique identifier of the MCP proxy| +|deploymentId|path|string(uuid)|true|The UUID of the deployment| + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Deployment deleted successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Undeploy deployment from gateway + + + +`POST /mcp-proxies/{mcpProxyId}/deployments/{deploymentId}/undeploy` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/mcp-proxies/{mcpProxyId}/deployments/{deploymentId}/undeploy?gatewayId=string \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Undeploys an active deployment, stopping the MCP proxy from being served on the specified gateway. +The deployment artifact remains in the system and can be restored later. +Returns the updated deployment object with initial status UNDEPLOYING. Final status (UNDEPLOYED or FAILED) will be reported asynchronously via the deployment's status and statusReason once the gateway acknowledges. + +The gatewayId query parameter is validated against the deployment's bound gateway to prevent unintended operations. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|mcpProxyId|path|string|true|Unique identifier of the MCP proxy| +|deploymentId|path|string|true|UUID of the deployment to undeploy| +|gatewayId|query|string|true|Handle (URL-friendly slug) of the gateway (validated against deployment's bound gateway)| + +> Example responses + +> 200 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Undeploy initiated successfully. Returns the deployment with initial status UNDEPLOYING. Poll status for final result.|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Restore a previous deployment + + + +`POST /mcp-proxies/{mcpProxyId}/deployments/{deploymentId}/restore` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/mcp-proxies/{mcpProxyId}/deployments/{deploymentId}/restore?gatewayId=string \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Initiates restoring a previous deployment (ARCHIVED or UNDEPLOYED) on the specified gateway. +Returns the deployment with initial status DEPLOYING. Final success or failure will be reported asynchronously via the deployment's status and statusReason once the gateway acknowledges. +The target deployment must not already be in DEPLOYED status. + +The gatewayId query parameter is validated against the deployment's bound gateway to prevent unintended operations. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|mcpProxyId|path|string|true|Unique identifier of the MCP proxy| +|deploymentId|path|string|true|UUID of the deployment to restore (must be ARCHIVED or UNDEPLOYED)| +|gatewayId|query|string|true|Handle (URL-friendly slug) of the gateway (validated against deployment's bound gateway)| + +> Example responses + +> 200 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Restore initiated successfully. Returns the deployment with initial status DEPLOYING. Poll status for final result.|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/organizations.md b/docs/rest-apis/platform-api/organizations.md new file mode 100644 index 0000000000..bbb79fbd8a --- /dev/null +++ b/docs/rest-apis/platform-api/organizations.md @@ -0,0 +1,372 @@ +

Organizations

+ +Organization management operations + +## Register a new organization + + + +`POST /organizations` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/organizations \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Registers a new organization with a unique UUID and handle. +This endpoint is used during the organization onboarding process. +A valid Bearer JWT token with the `ap:organization:create` or `ap:organization:manage` scope is required. + +> Payload + +```json +{ + "displayName": "Acme Corporation", + "region": "us" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[Organization](schemas.md#schemaorganization)|true|Organization that needs to be added| + +> Example responses + +> 201 Response + +```json +{ + "id": "acme", + "displayName": "Acme Corporation", + "region": "us", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Organization registered successfully|[Organization](schemas.md#schemaorganization)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## List organizations + + + +`GET /organizations` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/organizations \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves the list of organizations. Results are returned in a paginated +collection envelope. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "id": "acme", + "displayName": "Acme Corporation", + "region": "us", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Organizations retrieved successfully|[OrganizationListResponse](schemas.md#schemaorganizationlistresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get organization by ID + + + +`GET /organizations/{organizationId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/organizations/{organizationId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Returns the organization with the specified handle (id). + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|organizationId|path|string|true|ID of the organization.| + +> Example responses + +> 200 Response + +```json +{ + "id": "acme", + "displayName": "Acme Corporation", + "region": "us", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Organization retrieved successfully|[Organization](schemas.md#schemaorganization)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Check if organization exists + + + +`HEAD /organizations/{organizationId}` + +> Code samples + +```shell + +curl -X HEAD https://localhost:9243/api/v0.9/organizations/{organizationId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Checks if an organization with the specified handle (id) exists. +Returns only headers without a response body. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|organizationId|path|string|true|ID of the organization.| + +> Example responses + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Organization exists|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/projects.md b/docs/rest-apis/platform-api/projects.md new file mode 100644 index 0000000000..228102abe8 --- /dev/null +++ b/docs/rest-apis/platform-api/projects.md @@ -0,0 +1,620 @@ +

Projects

+ +Project management operations + +## Create a new project + + + +`POST /projects` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/projects \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Creates a new project within the organization specified in the JWT token. +Organization ID is automatically extracted from the token and does not need to be provided. + +> Payload + +```json +{ + "id": "default-project", + "displayName": "Default Project", + "description": "This is the default project for development" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[CreateProjectRequest](schemas.md#schemacreateprojectrequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "id": "default-project", + "displayName": "Default Project", + "description": "This is the default project for development", + "organizationId": "acme", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Project created successfully|[Project](schemas.md#schemaproject)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## Get all projects for current user's organization + + + +`GET /projects` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/projects \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves all projects belonging to the organization specified in the JWT token. +Organization ID is automatically extracted from the token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| +|sortBy|query|string|false|Field to sort the collection by. An unrecognized value falls back to the default sort (createdAt).| +|sortOrder|query|string|false|Sort direction applied to `sortBy`.| +|query|query|string|false|Case-insensitive substring filter matched against the resource id (handle).| + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|sortBy|name| +|sortBy|createdAt| +|sortOrder|asc| +|sortOrder|desc| + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "id": "default-project", + "displayName": "Default Project", + "description": "This is the default project for development", + "organizationId": "acme", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Projects retrieved successfully|[ProjectListResponse](schemas.md#schemaprojectlistresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get project by UUID + + + +`GET /projects/{projectId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/projects/{projectId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves a specific project by its UUID. Access is validated against the organization +in the JWT token to ensure users can only access projects in their organization. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|projectId|path|string|true|**Project ID** (handle — unique slug identifier) of the Project.| + +#### Detailed descriptions + +**projectId**: **Project ID** (handle — unique slug identifier) of the Project. + +> Example responses + +> 200 Response + +```json +{ + "id": "default-project", + "displayName": "Default Project", + "description": "This is the default project for development", + "organizationId": "acme", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Project retrieved successfully|[Project](schemas.md#schemaproject)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Update project + + + +`PUT /projects/{projectId}` + +> Code samples + +```shell + +curl -X PUT https://localhost:9243/api/v0.9/projects/{projectId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Updates an existing project's name. Access is validated against the organization +in the JWT token. + +> Payload + +```json +{ + "displayName": "Default Project", + "description": "This is the default project for development" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|projectId|path|string|true|**Project ID** (handle — unique slug identifier) of the Project.| +|body|body|[Project](schemas.md#schemaproject)|true|none| + +#### Detailed descriptions + +**projectId**: **Project ID** (handle — unique slug identifier) of the Project. + +> Example responses + +> 200 Response + +```json +{ + "id": "default-project", + "displayName": "Default Project", + "description": "This is the default project for development", + "organizationId": "acme", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Project updated successfully|[Project](schemas.md#schemaproject)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete project + + + +`DELETE /projects/{projectId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/projects/{projectId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Deletes a specific project by its UUID. Access is validated against the organization +in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|projectId|path|string|true|**Project ID** (handle — unique slug identifier) of the Project.| + +#### Detailed descriptions + +**projectId**: **Project ID** (handle — unique slug identifier) of the Project. + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Project deleted successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/rest-api-deployments.md b/docs/rest-apis/platform-api/rest-api-deployments.md new file mode 100644 index 0000000000..cbd7613fe9 --- /dev/null +++ b/docs/rest-apis/platform-api/rest-api-deployments.md @@ -0,0 +1,789 @@ +

REST API Deployments

+ +API deployment artifact management and lifecycle operations + +## Create and deploy a new deployment + + + +`POST /rest-apis/{restApiId}/deployments` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/rest-apis/{restApiId}/deployments \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Creates an immutable deployment artifact for an API and deploys it to a specified gateway. +Each deployment targets a single gateway. The apiId parameter is the API handle (identifier), +not the UUID. The operation returns a transitional DEPLOYING status. Final success or failure will be reported asynchronously via the deployment's status and statusReason once the gateway acknowledges. +Access is validated against the organization in the JWT token. + +> Payload + +```json +{ + "name": "v1.0-production", + "base": "current", + "gatewayId": "prod-gateway-01", + "metadata": {} +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| +|body|body|[DeployRequest](schemas.md#schemadeployrequest)|true|Deployment request with gateway ID, base reference, and metadata| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +> Example responses + +> 201 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|API deployed successfully|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## Get deployments for a REST API + + + +`GET /rest-apis/{restApiId}/deployments` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/rest-apis/{restApiId}/deployments \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves all deployment artifacts for a specific API. The apiId parameter is the API handle (identifier), +not the UUID. Supports filtering by gateway UUID and deployment status. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| +|gatewayId|query|string|false|**Gateway ID** consisting of the **UUID** of the Gateway to filter status by.| +|status|query|string|false|Filter deployments by status (DEPLOYED, UNDEPLOYED, DEPLOYING, UNDEPLOYING, FAILED, or ARCHIVED)| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +**gatewayId**: **Gateway ID** consisting of the **UUID** of the Gateway to filter status by. + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|status|DEPLOYED| +|status|UNDEPLOYED| +|status|DEPLOYING| +|status|UNDEPLOYING| +|status|FAILED| +|status|ARCHIVED| + +> Example responses + +> 200 Response + +```json +{ + "count": 0, + "list": [ + { + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Deployments retrieved successfully|[DeploymentListResponse](schemas.md#schemadeploymentlistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get deployment by ID + + + +`GET /rest-apis/{restApiId}/deployments/{deploymentId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/rest-apis/{restApiId}/deployments/{deploymentId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves metadata for a specific deployment artifact including status, gateway association, +and timestamps. Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| +|deploymentId|path|string(uuid)|true|The UUID of the deployment| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +> Example responses + +> 200 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Deployment metadata retrieved successfully|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete deployment + + + +`DELETE /rest-apis/{restApiId}/deployments/{deploymentId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/rest-apis/{restApiId}/deployments/{deploymentId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Deletes a deployment artifact. Deletion is only allowed when the deployment is in UNDEPLOYED status. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| +|deploymentId|path|string(uuid)|true|The UUID of the deployment| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Deployment deleted successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Undeploy deployment from gateway + + + +`POST /rest-apis/{restApiId}/deployments/{deploymentId}/undeploy` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/rest-apis/{restApiId}/deployments/{deploymentId}/undeploy?gatewayId=string \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Undeploys an active deployment, stopping the API from being served on the specified gateway. +The deployment artifact remains in the system and can be restored later. +Returns the updated deployment object with initial status UNDEPLOYING. Final status (UNDEPLOYED or FAILED) will be reported asynchronously via the deployment's status and statusReason once the gateway acknowledges. + +The gatewayId query parameter is validated against deployment's bound gateway to prevent unintended operations. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| +|deploymentId|path|string|true|UUID of the deployment to undeploy| +|gatewayId|query|string|true|Handle (URL-friendly slug) of the gateway (validated against deployment's bound gateway)| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +> Example responses + +> 200 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Undeploy initiated successfully. Returns the deployment with initial status UNDEPLOYING. Poll status for final result.|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Restore a previous deployment + + + +`POST /rest-apis/{restApiId}/deployments/{deploymentId}/restore` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/rest-apis/{restApiId}/deployments/{deploymentId}/restore?gatewayId=string \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Initiates restoring a previous deployment (ARCHIVED or UNDEPLOYED) on the specified gateway. +Returns the deployment with initial status DEPLOYING. Final success or failure will be reported asynchronously via the deployment's status and statusReason once the gateway acknowledges. +The target deployment must not already be in DEPLOYED status. + +The gatewayId query parameter is validated against the deployment's bound gateway to prevent unintended operations. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| +|deploymentId|path|string|true|UUID of the deployment to restore (must be ARCHIVED or UNDEPLOYED)| +|gatewayId|query|string|true|Handle (URL-friendly slug) of the gateway (validated against deployment's bound gateway)| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +> Example responses + +> 200 Response + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Restore initiated successfully. Returns the deployment with initial status DEPLOYING. Poll status for final result.|[DeploymentResponse](schemas.md#schemadeploymentresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/rest-apis.md b/docs/rest-apis/platform-api/rest-apis.md new file mode 100644 index 0000000000..07acbecdc3 --- /dev/null +++ b/docs/rest-apis/platform-api/rest-apis.md @@ -0,0 +1,1904 @@ +

REST APIs

+ +API management operations + +## Get all REST APIs for an organization + + + +`GET /rest-apis` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/rest-apis?projectId=default-project \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves all REST APIs belonging to an organization. +Requires the projectId query parameter to filter APIs by project. +Provide name and version together to check uniqueness (empty result = name/version combination is available). +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|projectId|query|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by.| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| +|sortBy|query|string|false|Field to sort the collection by. An unrecognized value falls back to the default sort (createdAt).| +|sortOrder|query|string|false|Sort direction applied to `sortBy`.| +|query|query|string|false|Case-insensitive substring filter matched against the resource id (handle).| + +#### Detailed descriptions + +**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by. + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|sortBy|name| +|sortBy|createdAt| +|sortOrder|asc| +|sortOrder|desc| + +> Example responses + +> 200 Response + +```json +{ + "count": 2, + "list": [ + { + "id": "my-rest-api-handle", + "displayName": "PizzaShackAPI", + "description": "This is a simple API for Pizza Shack online pizza delivery store", + "context": "pizza", + "version": "1.0.0", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "projectId": "default-project", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z", + "readOnly": false, + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + } + }, + "lifeCycleStatus": "CREATED", + "kind": "RestApi", + "transport": [ + "http", + "https" + ], + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operations": [ + { + "name": "getPetById", + "description": "Find pet by ID", + "request": { + "method": "GET", + "path": "/pet/{petId}", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "channels": [ + { + "name": "issues", + "description": "Channel for order events", + "request": { + "method": "SUB", + "name": "issues", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "subscriptionPlans": [ + "Gold", + "Silver" + ] + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|REST APIs retrieved successfully|[RESTAPIListResponse](schemas.md#schemarestapilistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Create a new REST API + + + +`POST /rest-apis` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/rest-apis \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Creates a new REST API in the platform. The API is associated with a project, which must +belong to the organization specified in the JWT token. + +> Payload + +```json +{ + "id": "my-rest-api-handle", + "displayName": "PizzaShackAPI", + "description": "This is a simple API for Pizza Shack online pizza delivery store", + "context": "pizza", + "version": "1.0.0", + "projectId": "default-project", + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + } + }, + "lifeCycleStatus": "CREATED", + "kind": "RestApi", + "transport": [ + "http", + "https" + ], + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operations": [ + { + "name": "getPetById", + "description": "Find pet by ID", + "request": { + "method": "GET", + "path": "/pet/{petId}", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "channels": [ + { + "name": "issues", + "description": "Channel for order events", + "request": { + "method": "SUB", + "name": "issues", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "subscriptionPlans": [ + "Gold", + "Silver" + ] +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[CreateRESTAPIRequest](schemas.md#schemacreaterestapirequest)|true|API object that needs to be added| + +> Example responses + +> 201 Response + +```json +{ + "id": "my-rest-api-handle", + "displayName": "PizzaShackAPI", + "description": "This is a simple API for Pizza Shack online pizza delivery store", + "context": "pizza", + "version": "1.0.0", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "projectId": "default-project", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z", + "readOnly": false, + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + } + }, + "lifeCycleStatus": "CREATED", + "kind": "RestApi", + "transport": [ + "http", + "https" + ], + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operations": [ + { + "name": "getPetById", + "description": "Find pet by ID", + "request": { + "method": "GET", + "path": "/pet/{petId}", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "channels": [ + { + "name": "issues", + "description": "Channel for order events", + "request": { + "method": "SUB", + "name": "issues", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "subscriptionPlans": [ + "Gold", + "Silver" + ] +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|API created successfully|[RESTAPI](schemas.md#schemarestapi)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## Get REST API by UUID + + + +`GET /rest-apis/{restApiId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/rest-apis/{restApiId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves a specific API by its UUID. Access is validated against the organization +in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +> Example responses + +> 200 Response + +```json +{ + "id": "my-rest-api-handle", + "displayName": "PizzaShackAPI", + "description": "This is a simple API for Pizza Shack online pizza delivery store", + "context": "pizza", + "version": "1.0.0", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "projectId": "default-project", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z", + "readOnly": false, + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + } + }, + "lifeCycleStatus": "CREATED", + "kind": "RestApi", + "transport": [ + "http", + "https" + ], + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operations": [ + { + "name": "getPetById", + "description": "Find pet by ID", + "request": { + "method": "GET", + "path": "/pet/{petId}", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "channels": [ + { + "name": "issues", + "description": "Channel for order events", + "request": { + "method": "SUB", + "name": "issues", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "subscriptionPlans": [ + "Gold", + "Silver" + ] +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API retrieved successfully|[RESTAPI](schemas.md#schemarestapi)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Update REST API + + + +`PUT /rest-apis/{restApiId}` + +> Code samples + +```shell + +curl -X PUT https://localhost:9243/api/v0.9/rest-apis/{restApiId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Updates an existing API's details. Access is validated against the organization +in the JWT token. + +> Payload + +```json +{ + "id": "my-rest-api-handle", + "displayName": "PizzaShackAPI", + "description": "This is a simple API for Pizza Shack online pizza delivery store", + "context": "pizza", + "version": "1.0.0", + "projectId": "default-project", + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + } + }, + "lifeCycleStatus": "CREATED", + "kind": "RestApi", + "transport": [ + "http", + "https" + ], + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operations": [ + { + "name": "getPetById", + "description": "Find pet by ID", + "request": { + "method": "GET", + "path": "/pet/{petId}", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "channels": [ + { + "name": "issues", + "description": "Channel for order events", + "request": { + "method": "SUB", + "name": "issues", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "subscriptionPlans": [ + "Gold", + "Silver" + ] +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| +|body|body|[RESTAPI](schemas.md#schemarestapi)|true|API object that needs to be updated| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +> Example responses + +> 200 Response + +```json +{ + "id": "my-rest-api-handle", + "displayName": "PizzaShackAPI", + "description": "This is a simple API for Pizza Shack online pizza delivery store", + "context": "pizza", + "version": "1.0.0", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "projectId": "default-project", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z", + "readOnly": false, + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key" + } + } + }, + "lifeCycleStatus": "CREATED", + "kind": "RestApi", + "transport": [ + "http", + "https" + ], + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operations": [ + { + "name": "getPetById", + "description": "Find pet by ID", + "request": { + "method": "GET", + "path": "/pet/{petId}", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "channels": [ + { + "name": "issues", + "description": "Channel for order events", + "request": { + "method": "SUB", + "name": "issues", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "subscriptionPlans": [ + "Gold", + "Silver" + ] +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API updated successfully|[RESTAPI](schemas.md#schemarestapi)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete REST API + + + +`DELETE /rest-apis/{restApiId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/rest-apis/{restApiId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Deletes a specific API by its UUID. Access is validated against the organization +in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|API deleted successfully|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get gateways for REST API + + + +`GET /rest-apis/{restApiId}/gateways` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/rest-apis/{restApiId}/gateways \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Retrieves all gateways associated with the specified API, including deployment details. +Returns gateway information along with association timestamps and deployment status. +Access is validated against the organization in the JWT token. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +> Example responses + +> 200 Response + +```json +{ + "count": 3, + "list": [ + { + "id": "prod-gateway-01", + "organizationId": "acme", + "displayName": "Production Gateway 01", + "description": "Production gateway for handling API traffic", + "properties": { + "region": "us-west", + "tier": "premium" + }, + "endpoints": [ + "https://api.example.com:8443/api/v1", + "wss://events.example.com:8444" + ], + "isCritical": true, + "functionalityType": "regular", + "version": "1.0", + "isActive": true, + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-10-14T10:30:00Z", + "updatedAt": "2025-10-14T10:30:00Z", + "associatedAt": "2025-10-15T10:30:00Z", + "isDeployed": true, + "deployment": { + "status": "CREATED", + "deployedAt": "2025-10-15T11:00:00Z" + } + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of gateways associated with the API, including deployment details|[RESTAPIGatewayListResponse](schemas.md#schemarestapigatewaylistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Add gateways for REST API + + + +`POST /rest-apis/{restApiId}/gateways` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/rest-apis/{restApiId}/gateways \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Associates gateways to the specified API. If gateways are already associated, +updates the association timestamp. Returns all gateways associated with the API +including deployment details. Access is validated against the organization +in the JWT token. + +> Payload + +```json +[ + { + "gatewayId": "prod-gateway-01" + } +] +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| +|body|body|[AddGatewayToRESTAPIRequest](schemas.md#schemaaddgatewaytorestapirequest)|false|List of gateways to associate with the API| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +> Example responses + +> 200 Response + +```json +{ + "count": 3, + "list": [ + { + "id": "prod-gateway-01", + "organizationId": "acme", + "displayName": "Production Gateway 01", + "description": "Production gateway for handling API traffic", + "properties": { + "region": "us-west", + "tier": "premium" + }, + "endpoints": [ + "https://api.example.com:8443/api/v1", + "wss://events.example.com:8444" + ], + "isCritical": true, + "functionalityType": "regular", + "version": "1.0", + "isActive": true, + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-10-14T10:30:00Z", + "updatedAt": "2025-10-14T10:30:00Z", + "associatedAt": "2025-10-15T10:30:00Z", + "isDeployed": true, + "deployment": { + "status": "CREATED", + "deployedAt": "2025-10-15T11:00:00Z" + } + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of all gateways associated with the API, including deployment details|[RESTAPIGatewayListResponse](schemas.md#schemarestapigatewaylistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Create API key + + + +`POST /rest-apis/{restApiId}/api-keys` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/rest-apis/{restApiId}/api-keys \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Creates a new API key for the specified API. The API key will be hashed before +storage and broadcasted to all gateways where the API is deployed. This endpoint +allows external platforms to inject API keys to hybrid gateways. + +> Payload + +```json +{ + "id": "production-key-01", + "displayName": "Production API Key", + "apiKey": "sk_example_1234567890abcdef", + "externalRefId": "ext-ref-12345", + "expiresAt": "2026-12-31T23:59:59Z", + "expiresIn": { + "duration": 30, + "unit": "days" + }, + "issuer": "api-platform-devportal" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| +|body|body|[CreateAPIKeyRequest](schemas.md#schemacreateapikeyrequest)|true|API key creation request| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +> Example responses + +> 201 Response + +```json +{ + "status": "success", + "message": "API key created and broadcasted to gateways successfully", + "keyId": "production-key-01" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +> 503 Response + +```json +{ + "status": "error", + "code": "GATEWAY_CONNECTION_UNAVAILABLE", + "message": "No gateway connections are currently available.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|API key created successfully|[CreateAPIKeyResponse](schemas.md#schemacreateapikeyresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|Service Unavailable. No gateway connections are currently available to service this request.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## Update API key + + + +`PUT /rest-apis/{restApiId}/api-keys/{apiKeyId}` + +> Code samples + +```shell + +curl -X PUT https://localhost:9243/api/v0.9/rest-apis/{restApiId}/api-keys/{apiKeyId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Updates an existing API key for the specified API. The new API key value will +be hashed before storage and broadcasted to all gateways where the API is deployed. +This endpoint allows external platforms to rotate API keys on hybrid gateways. + +> Payload + +```json +{ + "name": "production-key-01", + "displayName": "Production API Key (Updated)", + "apiKey": "sk_example_new1234567890abcdef", + "externalRefId": "ext-ref-12345", + "expiresAt": "2027-12-31T23:59:59Z", + "expiresIn": { + "duration": 30, + "unit": "days" + }, + "issuer": "api-platform-devportal" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| +|apiKeyId|path|string|true|The unique name/identifier of the API key| +|body|body|[UpdateAPIKeyRequest](schemas.md#schemaupdateapikeyrequest)|true|API key update request| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "API key updated and broadcasted to gateways successfully", + "keyId": "production-key-01" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +> 503 Response + +```json +{ + "status": "error", + "code": "GATEWAY_CONNECTION_UNAVAILABLE", + "message": "No gateway connections are currently available.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key updated successfully|[UpdateAPIKeyResponse](schemas.md#schemaupdateapikeyresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|Service Unavailable. No gateway connections are currently available to service this request.|[Error](schemas.md#schemaerror)| + +## Revoke API key + + + +`DELETE /rest-apis/{restApiId}/api-keys/{apiKeyId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/rest-apis/{restApiId}/api-keys/{apiKeyId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Revokes an API key for the specified API. The revocation will be broadcasted +to all gateways where the API is deployed. This endpoint allows external platforms +to revoke API keys on hybrid gateways. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| +|apiKeyId|path|string|true|The unique name/identifier of the API key to revoke| + +#### Detailed descriptions + +**restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +> 503 Response + +```json +{ + "status": "error", + "code": "GATEWAY_CONNECTION_UNAVAILABLE", + "message": "No gateway connections are currently available.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|API key revoked successfully (no content)|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|Service Unavailable. No gateway connections are currently available to service this request.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/schemas.md b/docs/rest-apis/platform-api/schemas.md new file mode 100644 index 0000000000..63ab2d2804 --- /dev/null +++ b/docs/rest-apis/platform-api/schemas.md @@ -0,0 +1,5413 @@ +# Schemas + +

APIKeyItem

+ + + + + + +```json +{ + "id": "string", + "displayName": "string", + "maskedApiKey": "string", + "status": "active", + "createdAt": "2019-08-24T14:15:22Z", + "createdBy": "john.doe", + "updatedAt": "2019-08-24T14:15:22Z", + "expiresAt": "2019-08-24T14:15:22Z", + "issuer": "api-platform-devportal", + "allowedTargets": "string" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|URL-safe handle (identifier) of the API key (generated from displayName when not supplied)| +|displayName|string|true|none|Human-readable display name of the API key| +|maskedApiKey|string|true|none|Masked representation of the API key for display purposes| +|status|string|true|none|Current status of the key| +|createdAt|string(date-time)|true|none|Timestamp when the key was created| +|createdBy|string|true|read-only|User identifier of the user who created this resource| +|updatedAt|string(date-time)|true|none|Timestamp when the key was last updated| +|expiresAt|string(date-time)|false|none|Optional expiration timestamp| +|issuer|string|false|none|Optional identifier of the developer portal that provisioned this key| +|allowedTargets|string|true|none|Comma-separated list of allowed gateways; 'ALL' means unrestricted| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|active| +|status|revoked| +|status|expired| + +

UserAPIKeyItem

+ + + + + + +```json +{ + "id": "string", + "displayName": "string", + "maskedApiKey": "string", + "status": "active", + "createdAt": "2019-08-24T14:15:22Z", + "createdBy": "john.doe", + "updatedAt": "2019-08-24T14:15:22Z", + "expiresAt": "2019-08-24T14:15:22Z", + "issuer": "api-platform-devportal", + "allowedTargets": "string", + "artifactId": "wso2-openai-provider", + "artifactType": "RestApi" +} + +``` + +### Properties + +allOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[APIKeyItem](#schemaapikeyitem)|false|none|none| + +and + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|» artifactId|string|true|none|Handle (ID) of the artifact this key belongs to| +|» artifactType|string|true|none|Type of the artifact this key belongs to| + +#### Enumerated Values + +|Property|Value| +|---|---| +|artifactType|RestApi| +|artifactType|LlmProvider| +|artifactType|LlmProxy| + +

UserAPIKeyListResponse

+ + + + + + +```json +{ + "list": [ + { + "id": "string", + "displayName": "string", + "maskedApiKey": "string", + "status": "active", + "createdAt": "2019-08-24T14:15:22Z", + "createdBy": "john.doe", + "updatedAt": "2019-08-24T14:15:22Z", + "expiresAt": "2019-08-24T14:15:22Z", + "issuer": "api-platform-devportal", + "allowedTargets": "string", + "artifactId": "wso2-openai-provider", + "artifactType": "RestApi" + } + ], + "count": 0, + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|list|[[UserAPIKeyItem](#schemauserapikeyitem)]|true|none|List of API keys| +|count|integer|true|none|Number of API keys in current response| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

LLMProviderAPIKeyListResponse

+ + + + + + +```json +{ + "list": [ + { + "id": "string", + "displayName": "string", + "maskedApiKey": "string", + "status": "active", + "createdAt": "2019-08-24T14:15:22Z", + "createdBy": "john.doe", + "updatedAt": "2019-08-24T14:15:22Z", + "expiresAt": "2019-08-24T14:15:22Z", + "issuer": "api-platform-devportal", + "allowedTargets": "string" + } + ], + "count": 0, + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|list|[[APIKeyItem](#schemaapikeyitem)]|true|none|List of API keys| +|count|integer|true|none|Number of API keys in current response| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

LLMProxyAPIKeyListResponse

+ + + + + + +```json +{ + "list": [ + { + "id": "string", + "displayName": "string", + "maskedApiKey": "string", + "status": "active", + "createdAt": "2019-08-24T14:15:22Z", + "createdBy": "john.doe", + "updatedAt": "2019-08-24T14:15:22Z", + "expiresAt": "2019-08-24T14:15:22Z", + "issuer": "api-platform-devportal", + "allowedTargets": "string" + } + ], + "count": 0, + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|list|[[APIKeyItem](#schemaapikeyitem)]|true|none|List of API keys| +|count|integer|true|none|Number of API keys in current response| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

Organization

+ + + + + + +```json +{ + "id": "acme", + "displayName": "Acme Corporation", + "region": "us", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|read-only|Handle (URL-friendly slug) for the organization| +|displayName|string|true|none|Human-readable name for the organization| +|region|string|true|none|Geographic region where the organization operates| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|updatedBy|string|false|read-only|User identifier of the user who last updated this resource| +|createdAt|string(date-time)|false|read-only|Timestamp when the organization was created| +|updatedAt|string(date-time)|false|read-only|Timestamp when the organization was last updated| + +

CreateProjectRequest

+ + + + + + +```json +{ + "id": "default-project", + "displayName": "Default Project", + "description": "This is the default project for development" +} + +``` + +Request body for creating a project. Organization ID is automatically extracted +from the JWT token and does not need to be provided. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Handle (URL-friendly slug) for the project. Auto-generated from displayName if not provided.| +|displayName|string|true|none|Human-readable name for the project| +|description|string|false|none|Description of the project| + +

Project

+ + + + + + +```json +{ + "id": "default-project", + "displayName": "Default Project", + "description": "This is the default project for development", + "organizationId": "acme", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|read-only|Handle (URL-friendly slug) for the project| +|displayName|string|true|none|Human-readable name for the project| +|description|string|false|none|Description of the project| +|organizationId|string|true|read-only|Handle (URL-friendly slug) of the organization this project belongs to| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /projects/{projectId}), omitted from list responses.| +|createdAt|string(date-time)|false|read-only|Timestamp when the project was created| +|updatedAt|string(date-time)|false|read-only|Timestamp when the project was last updated| + +

ApplicationType

+ + + + + + +```json +"genai" + +``` + +Type of the application + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|string|false|none|Type of the application| + +#### Enumerated Values + +|Property|Value| +|---|---| +|*anonymous*|genai| + +

CreateApplicationRequest

+ + + + + + +```json +{ + "id": "my-app-handle", + "displayName": "GenAI Demo App", + "projectId": "default-project", + "type": "genai", + "description": "Sample GenAI application" +} + +``` + +Request body for creating an application. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique handle/identifier for the application. Can be provided during creation or auto-generated.| +|displayName|string|true|none|Human-readable name for the application| +|projectId|string|true|none|Handle (URL-friendly slug) of the project this application belongs to.| +|type|[ApplicationType](#schemaapplicationtype)|true|none|Type of the application| +|description|string|false|none|Description of the application| + +

Application

+ + + + + + +```json +{ + "id": "my-app-handle", + "displayName": "GenAI Demo App", + "projectId": "default-project", + "type": "genai", + "description": "Sample GenAI application", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|Application handle/identifier| +|displayName|string|true|none|Human-readable name for the application| +|projectId|string|true|none|Handle (URL-friendly slug) of the project this application belongs to| +|type|[ApplicationType](#schemaapplicationtype)|true|none|Type of the application| +|description|string|false|none|Description of the application| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /applications/{appId}), omitted from list responses.| +|createdAt|string(date-time)|false|read-only|none| +|updatedAt|string(date-time)|false|read-only|none| + +

ApplicationListResponse

+ + + + + + +```json +{ + "count": 2, + "list": [ + { + "id": "my-app-handle", + "displayName": "GenAI Demo App", + "projectId": "default-project", + "type": "genai", + "description": "Sample GenAI application", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|Number of items in current response| +|list|[[Application](#schemaapplication)]|true|none|none| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

AddApplicationAPIKeysRequest

+ + + + + + +```json +{ + "apiKeys": [ + { + "keyId": "client-key-1", + "associatedEntity": { + "id": "pizza-api" + } + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiKeys|[[APIKeyMappingSelector](#schemaapikeymappingselector)]|true|none|List of API key selectors to add to the application mappings| + +

AddApplicationAssociationsRequest

+ + + + + + +```json +{ + "associations": [ + { + "id": "provider-handle", + "kind": "LlmProvider" + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|associations|[[ApplicationAssociationSelector](#schemaapplicationassociationselector)]|true|none|List of association selectors to add to the application| + +

ApplicationAssociationSelector

+ + + + + + +```json +{ + "id": "provider-handle", + "kind": "LlmProvider" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|ID or handle of the association target| +|kind|string|true|none|Type of the association target| + +#### Enumerated Values + +|Property|Value| +|---|---| +|kind|LlmProvider| +|kind|LlmProxy| + +

ApplicationAssociation

+ + + + + + +```json +{ + "id": "provider-handle", + "displayName": "OpenAI Provider", + "version": "v1.0", + "kind": "LlmProvider", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|Handle/ID of the associated target| +|displayName|string|true|none|Human-readable name for the associated target| +|version|string|true|none|Version of the associated target| +|kind|string|true|none|Type of associated target| +|createdAt|string(date-time)|false|none|Timestamp when the association was created| +|updatedAt|string(date-time)|false|none|Timestamp when the association was updated| + +

ApplicationAssociationListResponse

+ + + + + + +```json +{ + "count": 2, + "list": [ + { + "id": "provider-handle", + "displayName": "OpenAI Provider", + "version": "v1.0", + "kind": "LlmProvider", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|Number of items in current response| +|list|[[ApplicationAssociation](#schemaapplicationassociation)]|true|none|none| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

APIKeyMappingSelector

+ + + + + + +```json +{ + "keyId": "client-key-1", + "associatedEntity": { + "id": "pizza-api" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|keyId|string|true|none|API key name| +|associatedEntity|[APIKeyMappingAssociatedEntity](#schemaapikeymappingassociatedentity)|true|none|none| + +

APIKeyMappingAssociatedEntity

+ + + + + + +```json +{ + "id": "pizza-api" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|ID of the entity that owns the API key| + +

AssociatedEntity

+ + + + + + +```json +{ + "id": "pizza-api", + "kind": "RestApi" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|ID of the associated entity| +|kind|string|true|none|Type of the associated entity| + +

MappedAPIKey

+ + + + + + +```json +{ + "keyId": "client-key-1", + "associatedEntity": { + "id": "pizza-api", + "kind": "RestApi" + }, + "status": "ACTIVE", + "userId": "john.doe", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z", + "expiresAt": "2026-11-15T10:30:00Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|keyId|string|true|none|Name of the API key| +|associatedEntity|[AssociatedEntity](#schemaassociatedentity)|true|none|none| +|status|string|false|none|Status of the API key| +|userId|string|false|read-only|User identifier of the user who created this resource| +|createdAt|string(date-time)|false|none|Timestamp when the API key was created| +|updatedAt|string(date-time)|false|none|Timestamp when the API key was updated| +|expiresAt|string(date-time)|false|none|Expiration timestamp of the API key| + +

MappedAPIKeyListResponse

+ + + + + + +```json +{ + "count": 2, + "list": [ + { + "keyId": "client-key-1", + "associatedEntity": { + "id": "pizza-api", + "kind": "RestApi" + }, + "status": "ACTIVE", + "userId": "john.doe", + "createdAt": "2025-11-15T10:30:00Z", + "updatedAt": "2025-11-15T11:30:00Z", + "expiresAt": "2026-11-15T10:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|Number of items in current response| +|list|[[MappedAPIKey](#schemamappedapikey)]|true|none|none| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

RESTAPI

+ + + + + + +```json +{ + "id": "my-rest-api-handle", + "displayName": "PizzaShackAPI", + "description": "This is a simple API for Pizza Shack online pizza delivery store", + "context": "pizza", + "version": "1.0.0", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "projectId": "default-project", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z", + "readOnly": false, + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + } + }, + "lifeCycleStatus": "CREATED", + "kind": "RestApi", + "transport": [ + "http", + "https" + ], + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operations": [ + { + "name": "getPetById", + "description": "Find pet by ID", + "request": { + "method": "GET", + "path": "/pet/{petId}", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "channels": [ + { + "name": "issues", + "description": "Channel for order events", + "request": { + "method": "SUB", + "name": "issues", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "subscriptionPlans": [ + "Gold", + "Silver" + ] +} + +``` + +API object + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique handle/identifier for the API. Can be provided during creation or auto-generated. On update (PUT), if provided must match the path parameter — returns 400 if they differ.| +|displayName|string|true|none|Human-readable name for the API| +|description|string|false|none|none| +|context|string|true|none|none| +|version|string|true|none|none| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /rest-apis/{apiId}), omitted from list responses.| +|projectId|string|true|none|Handle (URL-friendly slug) of the project this API belongs to| +|createdAt|string(date-time)|false|read-only|Timestamp when the api was created| +|updatedAt|string(date-time)|false|read-only|Timestamp when the api was last updated| +|readOnly|boolean|false|read-only|True if the artifact originated from a data-plane gateway (origin gateway_api) and is read-only in the control plane; false for control-plane created artifacts.| +|upstream|[Upstream](#schemaupstream)|true|none|Upstream backend configuration with main and sandbox endpoints| +|lifeCycleStatus|string|false|none|Current lifecycle status of the API| +|kind|string|false|none|Kind of the API based on its communication protocol or architectural style| +|transport|[string]|false|none|Supported transports for the API (http and/or https)| +|policies|[[Policy](#schemapolicy)]|false|none|List of policies to be applied on the API| +|operations|[[Operation](#schemaoperation)]|false|none|List of operations exposed by this API| +|channels|[[Channel](#schemachannel)]|false|none|List of channels exposed by this API| +|subscriptionPlans|[string]|false|none|List of subscription plan names enabled for this API (e.g. Gold, Silver).
When set, only these plans can be used when subscribing to the API.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|lifeCycleStatus|STAGED| +|lifeCycleStatus|CREATED| +|lifeCycleStatus|PUBLISHED| +|lifeCycleStatus|DEPRECATED| +|lifeCycleStatus|RETIRED| +|lifeCycleStatus|BLOCKED| + +

SecurityConfig

+ + + + + + +```json +{ + "enabled": true, + "apiKey": { + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" + } +} + +``` + +Security Configuration + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|enabled|boolean|false|none|Whether security is enabled| +|apiKey|[APIKeySecurity](#schemaapikeysecurity)|false|none|Configuration for API key based authentication| + +

APIKeySecurity

+ + + + + + +```json +{ + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" +} + +``` + +API Key Security + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|enabled|boolean|false|none|Whether API key authentication is enabled| +|key|string|false|none|Name of the header or query parameter to be used for the API key| +|valuePrefix|string|false|none|Optional prefix to strip from the inbound API key value before validation, for example "Bearer"| +|in|string|false|none|Location of the API key (header or query)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|in|header| +|in|query| + +

Operation

+ + + + + + +```json +{ + "name": "getPetById", + "description": "Find pet by ID", + "request": { + "method": "GET", + "path": "/pet/{petId}", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } +} + +``` + +API Operation + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|false|none|Name of the operation| +|description|string|false|none|Description of the operation| +|request|[OperationRequest](#schemaoperationrequest)|true|none|Request details for an API operation| + +

Channel

+ + + + + + +```json +{ + "name": "issues", + "description": "Channel for order events", + "request": { + "method": "SUB", + "name": "issues", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } +} + +``` + +Async API Channel + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|false|none|Name of the channel| +|description|string|false|none|Description of the channel| +|request|[ChannelRequest](#schemachannelrequest)|true|none|Request details for a channel within the Async API| + +

OperationRequest

+ + + + + + +```json +{ + "method": "GET", + "path": "/pet/{petId}", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] +} + +``` + +Operation Request + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|method|string|true|none|HTTP method for the operation| +|path|string|true|none|Resource path for the operation| +|policies|[[Policy](#schemapolicy)]|false|none|List of policies to be applied on the operation| + +#### Enumerated Values + +|Property|Value| +|---|---| +|method|GET| +|method|POST| +|method|PUT| +|method|DELETE| +|method|PATCH| +|method|HEAD| +|method|OPTIONS| + +

ChannelRequest

+ + + + + + +```json +{ + "method": "SUB", + "name": "issues", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] +} + +``` + +Channel Request + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|method|string|true|none|Async method for the channel| +|name|string|true|none|Channel name for the event| +|policies|[[Policy](#schemapolicy)]|false|none|List of policies to be applied on the operation| + +#### Enumerated Values + +|Property|Value| +|---|---| +|method|SUB| + +

Policy

+ + + + + + +```json +{ + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" +} + +``` + +Policy Configuration + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|executionCondition|string|false|none|Conditional expression that determines when this policy executes| +|name|string|true|none|Name of the policy to apply| +|params|object|false|none|Key-value pairs of parameters for the policy| +|version|string|true|none|Version of the policy. Only major-only version is allowed (e.g., v0, v1). Full semantic version (e.g., v1.0.0) is not accepted and will be rejected.| + +

AddGatewayToRESTAPIRequest

+ + + + + + +```json +{ + "gatewayId": "prod-gateway-01" +} + +``` + +AddGatewayToAPIRequest object with basic gateway details + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|gatewayId|string|true|none|Handle (URL-friendly slug) of the gateway to associate with the REST API| + +

RESTAPIGatewayListResponse

+ + + + + + +```json +{ + "count": 3, + "list": [ + { + "id": "prod-gateway-01", + "organizationId": "acme", + "displayName": "Production Gateway 01", + "description": "Production gateway for handling API traffic", + "properties": { + "region": "us-west", + "tier": "premium" + }, + "endpoints": [ + "https://api.example.com:8443/api/v1", + "wss://events.example.com:8444" + ], + "isCritical": true, + "functionalityType": "regular", + "version": "1.0", + "isActive": true, + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-10-14T10:30:00Z", + "updatedAt": "2025-10-14T10:30:00Z", + "associatedAt": "2025-10-15T10:30:00Z", + "isDeployed": true, + "deployment": { + "status": "CREATED", + "deployedAt": "2025-10-15T11:00:00Z" + } + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|Number of gateways in current response| +|list|[[RESTAPIGatewayResponse](#schemarestapigatewayresponse)]|true|none|List of gateways associated with the API, including deployment details when deployed| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

RESTAPIGatewayResponse

+ + + + + + +```json +{ + "id": "prod-gateway-01", + "organizationId": "acme", + "displayName": "Production Gateway 01", + "description": "Production gateway for handling API traffic", + "properties": { + "region": "us-west", + "tier": "premium" + }, + "endpoints": [ + "https://api.example.com:8443/api/v1", + "wss://events.example.com:8444" + ], + "isCritical": true, + "functionalityType": "regular", + "version": "1.0", + "isActive": true, + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-10-14T10:30:00Z", + "updatedAt": "2025-10-14T10:30:00Z", + "associatedAt": "2025-10-15T10:30:00Z", + "isDeployed": true, + "deployment": { + "status": "CREATED", + "deployedAt": "2025-10-15T11:00:00Z" + } +} + +``` + +### Properties + +allOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[GatewayResponse](#schemagatewayresponse)|false|none|none| + +and + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|» associatedAt|string(date-time)|true|none|Timestamp when the gateway was associated with the API| +|» isDeployed|boolean|true|none|Whether the API is currently deployed to this gateway| +|» deployment|[RESTAPIDeploymentDetails](#schemarestapideploymentdetails)|false|none|Deployment details (only present when isDeployed is true)| + +

RESTAPIDeploymentDetails

+ + + + + + +```json +{ + "status": "CREATED", + "deployedAt": "2025-10-15T11:00:00Z" +} + +``` + +Details about API deployment to a specific gateway + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|true|none|Current deployment status| +|deployedAt|string(date-time)|true|none|Timestamp when the API was deployed| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|CREATED| +|status|APPROVED| +|status|REJECTED| + +

CreateGatewayRequest

+ + + + + + +```json +{ + "id": "prod-gateway-01", + "displayName": "Production Gateway 01", + "description": "Production gateway for handling API traffic", + "endpoints": [ + "https://api.example.com:8443/api/v1", + "wss://events.example.com:8444" + ], + "isCritical": true, + "functionalityType": "regular", + "properties": { + "region": "us-west", + "tier": "premium" + }, + "version": "1.0" +} + +``` + +Request body for creating a gateway. Organization ID is automatically extracted +from the JWT token and does not need to be provided. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Handle (URL-friendly slug) for the gateway. Immutable after creation.| +|displayName|string|true|none|Human-readable gateway name| +|description|string|false|none|Description of the gateway| +|endpoints|[string]|true|none|Network endpoints exposed by this gateway, each as a full URL string| +|isCritical|boolean|false|none|Whether the gateway is critical for production| +|functionalityType|string|true|none|Type of gateway functionality| +|properties|object|false|none|Custom key-value properties for the gateway| +|version|string|false|none|Gateway version in `major.minor` format (e.g. `1.0`) or CalVer `YYYY.MM.DD` format (e.g. `2026.05.13`). Defaults to `1.0` if not provided.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|functionalityType|regular| +|functionalityType|ai| +|functionalityType|event| + +

CustomPolicyResponse

+ + + + + + +```json +{ + "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "organizationUuid": "bc554ded-7e40-44a7-b397-48480793ad03", + "name": "rate-limit-custom", + "version": "1.0.0", + "description": "Custom rate limiting policy", + "policyDefinition": {}, + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} + +``` + +A custom policy stored in the platform's custom policy registry. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|uuid|string(uuid)|true|none|Unique identifier of the custom policy record| +|organizationUuid|string(uuid)|true|none|Organization this policy belongs to| +|name|string|true|none|Policy name| +|version|string|true|none|Policy version| +|description|string|false|none|Human-readable description of the policy| +|policyDefinition|object|true|none|The full policy schema as declared in the policy's policy-definition.yaml.
Contains `parameters` and `systemParameters` JSON Schema documents.| +|createdAt|string(date-time)|false|none|none| +|updatedAt|string(date-time)|false|none|none| + +

GatewayPolicyDefinition

+ + + + + + +```json +{ + "name": "set-wso2-headers", + "version": "v0.8.0", + "description": "Sets WSO2-specific headers in the request and response.", + "isCustomPolicy": true, + "policyDefinition": {} +} + +``` + +A policy installed on a gateway controller. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|Unique policy name| +|version|string|true|none|Semantic version of the policy| +|description|string|false|none|Human-readable description of the policy| +|isCustomPolicy|boolean|true|none|Whether this is a user-installed custom policy.| +|policyDefinition|object|false|none|The full policy schema as declared in the policy's policy-definition.yaml.
Contains `parameters` and `systemParameters` JSON Schema documents.
Only present for custom policies.| + +

ManifestSyncResponse

+ + + + + + +```json +{ + "policies": [ + { + "name": "set-wso2-headers", + "version": "v0.8.0", + "description": "Sets WSO2-specific headers in the request and response.", + "isCustomPolicy": true, + "policyDefinition": {} + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|policies|[[GatewayPolicyDefinition](#schemagatewaypolicydefinition)]|false|none|All policies installed on the gateway. Each entry includes name, version, and isCustomPolicy.
Custom policies additionally include policyDefinition with their parameters and systemParameters schemas.| + +

GatewayResponse

+ + + + + + +```json +{ + "id": "prod-gateway-01", + "organizationId": "acme", + "displayName": "Production Gateway 01", + "description": "Production gateway for handling API traffic", + "properties": { + "region": "us-west", + "tier": "premium" + }, + "endpoints": [ + "https://api.example.com:8443/api/v1", + "wss://events.example.com:8444" + ], + "isCritical": true, + "functionalityType": "regular", + "version": "1.0", + "isActive": true, + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-10-14T10:30:00Z", + "updatedAt": "2025-10-14T10:30:00Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|read-only|Handle (URL-friendly slug) for the gateway| +|organizationId|string|false|none|Handle (URL-friendly slug) of the organization this gateway belongs to| +|displayName|string|true|none|Human-readable gateway name| +|description|string|false|none|Description of the gateway| +|properties|object|false|none|Custom key-value properties for the gateway| +|endpoints|[string]|false|none|Network endpoints exposed by this gateway, each as a full URL string| +|isCritical|boolean|false|none|Whether the gateway is critical for production| +|functionalityType|string|false|none|Type of gateway functionality| +|version|string|false|none|Gateway version in `major.minor` format (e.g. `1.0`) or CalVer `YYYY.MM.DD` format (e.g. `2026.05.13`)| +|isActive|boolean|false|none|Indicates if the gateway is currently connected to the platform via WebSocket| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /gateways/{gatewayId}), omitted from list responses.| +|createdAt|string(date-time)|false|none|Timestamp when gateway was registered| +|updatedAt|string(date-time)|false|none|Timestamp when gateway was last updated| + +#### Enumerated Values + +|Property|Value| +|---|---| +|functionalityType|regular| +|functionalityType|ai| +|functionalityType|event| + +

Pagination

+ + + + + + +```json +{ + "total": 10, + "offset": 0, + "limit": 10 +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|total|integer|true|none|Total number of items available across all pages| +|offset|integer|true|none|Zero-based index of first item in current response| +|limit|integer|true|none|Maximum number of items returned per page| + +

GatewayListResponse

+ + + + + + +```json +{ + "count": 2, + "list": [ + { + "id": "prod-gateway-01", + "organizationId": "acme", + "displayName": "Production Gateway 01", + "description": "Production gateway for handling API traffic", + "properties": { + "region": "us-west", + "tier": "premium" + }, + "endpoints": [ + "https://api.example.com:8443/api/v1", + "wss://events.example.com:8444" + ], + "isCritical": true, + "functionalityType": "regular", + "version": "1.0", + "isActive": true, + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2025-10-14T10:30:00Z", + "updatedAt": "2025-10-14T10:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +GatewayListResponse for paginated gateway results + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|Number of items in current response| +|list|[[GatewayResponse](#schemagatewayresponse)]|true|none|none| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

GatewayStatusResponse

+ + + + + + +```json +{ + "id": "prod-gateway-01", + "isActive": true, + "isCritical": true +} + +``` + +Lightweight gateway status information optimized for frequent polling + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|read-only|Handle (URL-friendly slug) for the gateway| +|isActive|boolean|false|none|Indicates if the gateway is currently connected to the platform via WebSocket| +|isCritical|boolean|false|none|Whether the gateway is critical for production| + +

GatewayStatusListResponse

+ + + + + + +```json +{ + "count": 2, + "list": [ + { + "id": "prod-gateway-01", + "isActive": true, + "isCritical": true + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +List of gateway status information for polling + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|Number of items in current response| +|list|[[GatewayStatusResponse](#schemagatewaystatusresponse)]|true|none|[Lightweight gateway status information optimized for frequent polling]| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

ProjectListResponse

+ + + + + + +```json +{ + "count": 2, + "list": [ + { + "id": "default-project", + "displayName": "Default Project", + "description": "This is the default project for development", + "organizationId": "acme", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|Number of items in current response| +|list|[[Project](#schemaproject)]|true|none|none| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

OrganizationListResponse

+ + + + + + +```json +{ + "count": 2, + "list": [ + { + "id": "acme", + "displayName": "Acme Corporation", + "region": "us", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|Number of items in current response| +|list|[[Organization](#schemaorganization)]|true|none|none| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

RESTAPIListResponse

+ + + + + + +```json +{ + "count": 2, + "list": [ + { + "id": "my-rest-api-handle", + "displayName": "PizzaShackAPI", + "description": "This is a simple API for Pizza Shack online pizza delivery store", + "context": "pizza", + "version": "1.0.0", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "projectId": "default-project", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z", + "readOnly": false, + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + } + }, + "lifeCycleStatus": "CREATED", + "kind": "RestApi", + "transport": [ + "http", + "https" + ], + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operations": [ + { + "name": "getPetById", + "description": "Find pet by ID", + "request": { + "method": "GET", + "path": "/pet/{petId}", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "channels": [ + { + "name": "issues", + "description": "Channel for order events", + "request": { + "method": "SUB", + "name": "issues", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "subscriptionPlans": [ + "Gold", + "Silver" + ] + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|Number of items in current response| +|list|[[RESTAPI](#schemarestapi)]|true|none|none| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

TokenRotationResponse

+ + + + + + +```json +{ + "id": "def45678-g901-23hi-j456-789012klmnop", + "token": "nM8pQ1rT4vW7yZ0bC3eF6hI9kL2nO5qR8tU1wX4zA7cE0fH3jK6mP9sV2yB5dG8i", + "createdAt": "2025-10-15T14:20:00Z", + "message": "New token generated successfully. Old token remains active until revoked." +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string(uuid)|false|none|ID of the newly generated token| +|token|string|false|none|Plain-text new authentication token (only exposed once during rotation)| +|createdAt|string(date-time)|false|none|Timestamp when new token was created| +|message|string|false|none|Informational message about token rotation| + +

TokenInfoResponse

+ + + + + + +```json +{ + "id": "abc12345-f678-90de-f123-456789abcdef", + "status": "active", + "createdAt": "2025-10-14T10:30:00Z", + "revokedAt": null +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string(uuid)|false|none|Token UUID| +|status|string|false|none|Current token status| +|createdAt|string(date-time)|false|none|Timestamp when token was created| +|revokedAt|string(date-time)¦null|false|none|Timestamp when token was revoked (null if active)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|active| +|status|revoked| + +

CreateRESTAPIRequest

+ + + + + + +```json +{ + "id": "my-rest-api-handle", + "displayName": "PizzaShackAPI", + "description": "This is a simple API for Pizza Shack online pizza delivery store", + "context": "pizza", + "version": "1.0.0", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "projectId": "default-project", + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z", + "readOnly": false, + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + } + }, + "lifeCycleStatus": "CREATED", + "kind": "RestApi", + "transport": [ + "http", + "https" + ], + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operations": [ + { + "name": "getPetById", + "description": "Find pet by ID", + "request": { + "method": "GET", + "path": "/pet/{petId}", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "channels": [ + { + "name": "issues", + "description": "Channel for order events", + "request": { + "method": "SUB", + "name": "issues", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ] + } + } + ], + "subscriptionPlans": [ + "Gold", + "Silver" + ] +} + +``` + +### Properties + +allOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[RESTAPI](#schemarestapi)|false|none|none| + +and + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| + +

TimeUnit

+ + + + + + +```json +"days" + +``` + +Time unit for API key expiration duration + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|string|false|none|Time unit for API key expiration duration| + +#### Enumerated Values + +|Property|Value| +|---|---| +|*anonymous*|seconds| +|*anonymous*|minutes| +|*anonymous*|hours| +|*anonymous*|days| +|*anonymous*|weeks| +|*anonymous*|months| + +

ExpirationDuration

+ + + + + + +```json +{ + "duration": 30, + "unit": "days" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|duration|integer|true|none|Duration value (must be positive)| +|unit|[TimeUnit](#schematimeunit)|true|none|Time unit for API key expiration duration| + +

CreateAPIKeyRequest

+ + + + + + +```json +{ + "id": "production-key-01", + "displayName": "Production API Key", + "apiKey": "sk_example_1234567890abcdef", + "externalRefId": "ext-ref-12345", + "expiresAt": "2026-12-31T23:59:59Z", + "expiresIn": { + "duration": 30, + "unit": "days" + }, + "issuer": "api-platform-devportal" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique identifier for this API key within the API (optional; if omitted,
generated from displayName)| +|displayName|string|true|none|Human-readable name for the API key| +|apiKey|string|true|none|The plain text API key value that will be hashed before storage| +|externalRefId|string¦null|false|none|Optional reference ID for tracing purposes (from external platforms)| +|expiresAt|string(date-time)¦null|false|none|Optional expiration time in ISO 8601 format| +|expiresIn|[ExpirationDuration](#schemaexpirationduration)|false|none|Optional expiration duration| +|issuer|string¦null|false|none|Identifier of the developer portal that provisioned this API key. Null if not provided.| + +

CreateAPIKeyResponse

+ + + + + + +```json +{ + "status": "success", + "message": "API key created and broadcasted to gateways successfully", + "keyId": "production-key-01" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|true|none|Status of the operation| +|message|string|true|none|Additional details about the operation result| +|keyId|string|false|none|The internal ID generated for tracking| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|success| +|status|error| + +

UpdateAPIKeyRequest

+ + + + + + +```json +{ + "name": "production-key-01", + "displayName": "Production API Key (Updated)", + "apiKey": "sk_example_new1234567890abcdef", + "externalRefId": "ext-ref-12345", + "expiresAt": "2027-12-31T23:59:59Z", + "expiresIn": { + "duration": 30, + "unit": "days" + }, + "issuer": "api-platform-devportal" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|false|none|Unique identifier for this API key within the API (optional; if omitted, generated from displayName)| +|displayName|string|true|none|Human-readable name for the API key| +|apiKey|string|true|none|The new plain text API key value that will be hashed before storage| +|externalRefId|string¦null|false|none|Optional reference ID for tracing purposes (from external platforms)| +|expiresAt|string(date-time)¦null|false|none|Optional expiration time in ISO 8601 format| +|expiresIn|[ExpirationDuration](#schemaexpirationduration)|false|none|Optional expiration duration| +|issuer|string|false|none|Identifies the portal that created this key| + +

UpdateAPIKeyResponse

+ + + + + + +```json +{ + "status": "success", + "message": "API key updated and broadcasted to gateways successfully", + "keyId": "production-key-01" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|true|none|Status of the operation| +|message|string|true|none|Additional details about the operation result| +|keyId|string|false|none|The internal ID of the updated key| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|success| +|status|error| + +

SubscriptionPlanLimit

+ + + + + + +```json +{ + "limitType": "REQUEST_COUNT", + "timeUnit": "HOUR", + "timeAmount": 1, + "limitCount": 10000, + "limitCountUnit": "string", + "stopOnQuotaReach": true +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|limitType|string|false|none|Kind of quota this limit enforces. Only REQUEST_COUNT is currently enforced; other values are accepted by the schema for forward compatibility but are rejected by the API today.| +|timeUnit|string|true|none|Throttle window unit| +|timeAmount|integer|false|none|Number of timeUnit windows the limit applies over (e.g. 2 with timeUnit=HOUR means "per 2 hours")| +|limitCount|integer|true|none|Number of requests (or units, for BANDWIDTH/TOTAL_TOKEN_COUNT) allowed in the throttle window| +|limitCountUnit|string|false|none|Unit for limitCount when limitType is BANDWIDTH (e.g. MB, GB)| +|stopOnQuotaReach|boolean|false|none|Whether to block requests when this limit's quota is exhausted| + +#### Enumerated Values + +|Property|Value| +|---|---| +|limitType|REQUEST_COUNT| +|limitType|BANDWIDTH| +|limitType|TOTAL_TOKEN_COUNT| +|timeUnit|MINUTE| +|timeUnit|HOUR| +|timeUnit|DAY| +|timeUnit|MONTH| + +

CreateSubscriptionPlanRequest

+ + + + + + +```json +{ + "id": "gold", + "displayName": "Gold", + "limits": [ + { + "limitType": "REQUEST_COUNT", + "timeUnit": "HOUR", + "timeAmount": 1, + "limitCount": 10000, + "limitCountUnit": "string", + "stopOnQuotaReach": true + } + ], + "expiryTime": "2019-08-24T14:15:22Z", + "status": "ACTIVE" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Handle (URL-friendly slug) for the plan. Immutable after creation.| +|displayName|string|true|none|Human-readable name for the subscription plan| +|limits|[[SubscriptionPlanLimit](#schemasubscriptionplanlimit)]|false|none|Throttling limits for the plan (e.g. requests per hour, requests per month). The table backing this API already supports multiple limits per plan, but the platform-api currently only persists and enforces the first entry in this array; any additional entries are accepted but ignored.| +|expiryTime|string(date-time)|false|none|Optional plan expiry time (RFC3339)| +|status|string|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|ACTIVE| +|status|INACTIVE| + +

SubscriptionPlan

+ + + + + + +```json +{ + "id": "string", + "displayName": "string", + "limits": [ + { + "limitType": "REQUEST_COUNT", + "timeUnit": "HOUR", + "timeAmount": 1, + "limitCount": 10000, + "limitCountUnit": "string", + "stopOnQuotaReach": true + } + ], + "expiryTime": "2019-08-24T14:15:22Z", + "organizationId": "acme", + "status": "ACTIVE", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Handle (slug) for the subscription plan| +|displayName|string|true|none|Human-readable name for the subscription plan| +|limits|[[SubscriptionPlanLimit](#schemasubscriptionplanlimit)]|false|none|Throttling limits configured for the plan. Only one entry is currently supported and returned, even though the underlying storage allows multiple.| +|expiryTime|string(date-time)|false|none|none| +|organizationId|string|false|none|Handle (URL-friendly slug) of the organization this plan belongs to| +|status|string|false|none|none| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /subscription-plans/{planId}), omitted from list responses.| +|createdAt|string(date-time)|false|none|none| +|updatedAt|string(date-time)|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|ACTIVE| +|status|INACTIVE| + +

SubscriptionPlanListResponse

+ + + + + + +```json +{ + "list": [ + { + "id": "string", + "displayName": "string", + "limits": [ + { + "limitType": "REQUEST_COUNT", + "timeUnit": "HOUR", + "timeAmount": 1, + "limitCount": 10000, + "limitCountUnit": "string", + "stopOnQuotaReach": true + } + ], + "expiryTime": "2019-08-24T14:15:22Z", + "organizationId": "acme", + "status": "ACTIVE", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "count": 0, + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|list|[[SubscriptionPlan](#schemasubscriptionplan)]|true|none|List of subscription plans in current response| +|count|integer|true|none|Number of subscription plans in current response| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

CreateSubscriptionRequest

+ + + + + + +```json +{ + "artifactId": "my-rest-api", + "kind": "RestApi", + "subscriberId": "user-123", + "applicationId": "my-app-handle", + "subscriptionPlanId": "gold", + "status": "ACTIVE" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|artifactId|string|true|none|Handle (ID) of the artifact to subscribe to. Resolved against the table for the given kind.| +|kind|string|true|none|Type of the artifact identified by artifactId. Determines which artifact table artifactId is resolved against.| +|subscriberId|string|true|none|Unique subscriber identifier for the subscription (required)| +|applicationId|string|false|none|Handle (ID) of the application this subscription belongs to. Optional in token-based subscriptions.| +|subscriptionPlanId|string|false|none|Handle (slug) of the subscription plan. Links the subscription to rate limit and billing configuration.| +|status|string|false|none|Subscription status (default ACTIVE)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|kind|RestApi| +|kind|LlmProvider| +|kind|LlmProxy| +|kind|Mcp| +|status|ACTIVE| +|status|INACTIVE| +|status|REVOKED| + +

Subscription

+ + + + + + +```json +{ + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "artifactId": "my-rest-api", + "kind": "RestApi", + "subscriberId": "string", + "applicationId": "my-app-handle", + "subscriptionToken": "string", + "subscriptionPlanId": "gold", + "subscriptionPlanName": "string", + "organizationId": "acme", + "status": "ACTIVE", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string(uuid)|false|none|Subscription ID| +|artifactId|string|false|none|Handle (ID) of the subscribed artifact| +|kind|string|false|none|Type of the subscribed artifact| +|subscriberId|string|false|none|Unique subscriber identifier for this API (required)| +|applicationId|string|false|none|Handle (ID) of the application this subscription belongs to (optional for token-based subscriptions)| +|subscriptionToken|string|false|none|Opaque subscription token for API invocation via Subscription-Key header| +|subscriptionPlanId|string|false|none|Handle (slug) of the subscription plan| +|subscriptionPlanName|string|false|none|Subscription plan display name (e.g. Bronze, Gold)| +|organizationId|string|false|none|Handle (URL-friendly slug) of the organization this subscription belongs to| +|status|string|false|none|none| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /subscriptions/{subscriptionId}), omitted from list responses.| +|createdAt|string(date-time)|false|none|none| +|updatedAt|string(date-time)|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|kind|RestApi| +|kind|LlmProvider| +|kind|LlmProxy| +|kind|Mcp| +|status|ACTIVE| +|status|INACTIVE| +|status|REVOKED| + +

SubscriptionListResponse

+ + + + + + +```json +{ + "list": [ + { + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "artifactId": "my-rest-api", + "kind": "RestApi", + "subscriberId": "string", + "applicationId": "my-app-handle", + "subscriptionToken": "string", + "subscriptionPlanId": "gold", + "subscriptionPlanName": "string", + "organizationId": "acme", + "status": "ACTIVE", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "count": 0, + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|list|[[Subscription](#schemasubscription)]|true|none|List of subscriptions in current response| +|count|integer|true|none|Number of subscriptions in current response| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

Error

+ + + + + + +```json +{ + "status": "error", + "code": "REST_API_NOT_FOUND", + "message": "The requested REST API could not be found.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ], + "details": {}, + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} + +``` + +Standard error response + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|true|none|Always the literal "error".| +|code|string|true|none|Stable, machine-readable error code from the error catalog, in the form `_` (e.g. `REST_API_NOT_FOUND`). Clients and agents should branch on this, not on the HTTP status.| +|message|string|true|none|Human-readable description of the error.| +|errors|[[FieldError](#schemafielderror)]|false|none|Per-field validation failures. Present when the error is a validation failure.| +|details|object|false|none|Optional structured metadata specific to this error condition (e.g. the resources referencing a secret that blocked its deletion). Shape varies by `code`; absent when not applicable.| +|trackingId|string(uuid)|false|none|Correlation ID for server-side failures. Present only on 5xx responses; quote it when reporting the error so operators can find the corresponding server log entry.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|error| + +

FieldError

+ + + + + + +```json +{ + "field": "spec.context", + "message": "must start with /" +} + +``` + +Field-level validation error + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|field|string|true|none|Path of the offending field.| +|message|string|true|none|Why the field failed validation.| + +

DeployRequest

+ + + + + + +```json +{ + "name": "v1.0-production", + "base": "current", + "gatewayId": "prod-gateway-01", + "metadata": {} +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|Name/label for this deployment (e.g., "v1.0-prod", "hotfix-2024-01-15")| +|base|string|true|none|The source for the API definition. Can be "current" (latest working copy) or a deploymentId (existing deployment)| +|gatewayId|string|true|none|Handle (URL-friendly slug) of the target gateway for this deployment| +|metadata|object|false|none|Optional metadata for the deployment. Supported keys include `endpointUrl`, `vhostMain`, and `vhostSandbox`.| + +

DeploymentResponse

+ + + + + + +```json +{ + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|deploymentId|string(uuid)|true|none|Unique identifier for the deployment| +|name|string|true|none|Name/label for this deployment| +|gatewayId|string|true|none|Handle (URL-friendly slug) of the gateway| +|status|string|true|none|Current deployment lifecycle state:
- DEPLOYED: Currently active on the gateway
- UNDEPLOYED: Suspended but can be rolled back
- DEPLOYING: Deployment in progress, waiting for gateway acknowledgement
- UNDEPLOYING: Undeployment in progress, waiting for gateway acknowledgement
- FAILED: Deployment or undeployment failed (see statusReason for error code)
- ARCHIVED: Historical deployment, can be rolled back| +|baseDeploymentId|string(uuid)¦null|false|none|UUID of the base deployment this was created from| +|metadata|object|false|none|Metadata associated with the deployment| +|createdAt|string(date-time)|true|none|Timestamp when the deployment artifact was created| +|statusReason|string¦null|false|none|Error code explaining the failure reason. Null unless status is FAILED (e.g. DEPLOYMENT_TIMEOUT, GATEWAY_PROCESSING_ERROR)| +|updatedAt|string(date-time)¦null|false|none|Timestamp when the deployment status last changed (null for ARCHIVED deployments)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|DEPLOYED| +|status|UNDEPLOYED| +|status|DEPLOYING| +|status|UNDEPLOYING| +|status|FAILED| +|status|ARCHIVED| + +

DeploymentListResponse

+ + + + + + +```json +{ + "count": 0, + "list": [ + { + "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", + "name": "v1.0-production", + "gatewayId": "prod-gateway-01", + "status": "DEPLOYED", + "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", + "metadata": {}, + "createdAt": "2019-08-24T14:15:22Z", + "statusReason": "string", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|Number of deployments in current response| +|list|[[DeploymentResponse](#schemadeploymentresponse)]|true|none|List of deployments| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

Upstream

+ + + + + + +```json +{ + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + } +} + +``` + +Upstream backend configuration with main and sandbox endpoints + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|main|[UpstreamDefinition](#schemaupstreamdefinition)|true|none|Upstream endpoint configuration (single target or reference)| +|sandbox|[UpstreamDefinition](#schemaupstreamdefinition)|false|none|Upstream endpoint configuration (single target or reference)| + +

UpstreamDefinition

+ + + + + + +```json +{ + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } +} + +``` + +Upstream endpoint configuration (single target or reference) + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|url|string(uri)|false|none|Direct backend URL to route traffic to| +|ref|string|false|none|Reference to a predefined upstreamDefinition| +|auth|[UpstreamAuth](#schemaupstreamauth)|false|none|Authentication configuration for upstream endpoints| + +oneOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| + +xor + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| + +

UpstreamAuth

+ + + + + + +```json +{ + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" +} + +``` + +Authentication configuration for upstream endpoints + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|type|string|false|none|Authentication type| +|header|string|false|none|Header name for api-key authentication (e.g., 'Authorization' for bearer/basic, custom header for api-key)| +|value|string(password)|false|write-only|Authentication value (API key, Bearer token, or Base64 encoded credentials for basic auth)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|type|basic| +|type|bearer| +|type|api-key| +|type|other| +|type|none| + +

ExtractionIdentifier

+ + + + + + +```json +{ + "location": "payload", + "identifier": "$.usage.inputTokens" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|location|string|true|none|Where to find the token information| +|identifier|string|true|none|JSONPath expression or header name to identify the token value| + +#### Enumerated Values + +|Property|Value| +|---|---| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| + +

LLMProviderTemplateAuth

+ + + + + + +```json +{ + "type": "bearer", + "header": "Authorization", + "valuePrefix": "Bearer " +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|type|string|false|none|Authentication type used by the LLM provider template| +|header|string|false|none|Header name to send the auth value| +|valuePrefix|string|false|none|Prefix to attach before the auth value| + +

LLMProviderTemplateMetadata

+ + + + + + +```json +{ + "endpointUrl": "https://api.openai.com", + "auth": { + "type": "bearer", + "header": "Authorization", + "valuePrefix": "Bearer " + }, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "openapiSpecUrl": "https://api.openai.com/openapi.json" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|endpointUrl|string(uri)|false|none|Default endpoint URL for the template| +|auth|[LLMProviderTemplateAuth](#schemallmprovidertemplateauth)|false|none|none| +|logoUrl|string(uri)|false|none|URL of the provider logo| +|openapiSpecUrl|string(uri)|false|none|URL to the OpenAPI specification for the provider| + +

LLMProviderTemplateResourceMapping

+ + + + + + +```json +{ + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|resource|string|true|none|Resource path pattern this mapping applies to (for example /responses or /chat/*)| +|promptTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|completionTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|totalTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|remainingTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|requestModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|responseModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| + +

LLMProviderTemplateResourceMappings

+ + + + + + +```json +{ + "resources": [ + { + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|resources|[[LLMProviderTemplateResourceMapping](#schemallmprovidertemplateresourcemapping)]|false|none|none| + +

LLMProviderTemplate

+ + + + + + +```json +{ + "id": "openai", + "groupId": "openai", + "displayName": "OpenAI", + "managedBy": "wso2", + "description": "Default OpenAI template", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "readOnly": false, + "version": "v1.0", + "isLatest": true, + "enabled": true, + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "metadata": { + "endpointUrl": "https://api.openai.com", + "auth": { + "type": "bearer", + "header": "Authorization", + "valuePrefix": "Bearer " + }, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "openapiSpecUrl": "https://api.openai.com/openapi.json" + }, + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "resourceMappings": { + "resources": [ + { + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } + } + ] + }, + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique handle for the template| +|groupId|string|false|read-only|Stable identifier shared by every version of a template family.| +|displayName|string|true|none|Human-readable LLM Template name| +|managedBy|string|false|none|Identifies who manages the template. Built-in templates use 'wso2';
custom templates default to 'organization' and may be set to any value.| +|description|string|false|none|Description of the LLM provider template| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|updatedBy|string|false|read-only|User identifier of the user who last updated this resource| +|readOnly|boolean|false|read-only|True if the artifact originated from a data-plane gateway (origin gateway_api) and is read-only in the control plane; false for control-plane created artifacts.| +|version|string|true|none|Content version, matching the v. pattern (e.g. v1.0, v2.0).
Must be unique for this template.| +|isLatest|boolean|false|read-only|Whether this is the latest version of the template.| +|enabled|boolean|false|read-only|Whether this version is offered when creating providers. If false, the
template version is hidden from the provider creation UI and API.| +|openapi|string|false|none|OpenAPI specification content (JSON or YAML) for the provider, when
uploaded/pasted. Use metadata.openapiSpecUrl instead to reference the
spec by URL.| +|metadata|[LLMProviderTemplateMetadata](#schemallmprovidertemplatemetadata)|false|none|none| +|promptTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|completionTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|totalTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|remainingTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|requestModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|responseModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|resourceMappings|[LLMProviderTemplateResourceMappings](#schemallmprovidertemplateresourcemappings)|false|none|none| +|createdAt|string(date-time)|false|read-only|Timestamp when the resource was created| +|updatedAt|string(date-time)|false|read-only|Timestamp when the resource was last updated| + +

CreateLLMProviderTemplateVersionRequest

+ + + + + + +```json +{ + "displayName": "OpenAI", + "version": "v2.0", + "managedBy": "organization", + "description": "Default OpenAI template", + "openapi": "string", + "metadata": { + "endpointUrl": "https://api.openai.com", + "auth": { + "type": "bearer", + "header": "Authorization", + "valuePrefix": "Bearer " + }, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "openapiSpecUrl": "https://api.openai.com/openapi.json" + }, + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "resourceMappings": { + "resources": [ + { + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } + } + ] + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|displayName|string|false|none|Human-readable LLM Template name. Optional — when omitted, the new
version inherits the family's existing name. Supplying a different
value renames the template family.| +|version|string|true|none|New version identifier, e.g. v2.0. Must be unique for this template.| +|managedBy|string|false|none|Identifies who manages the template. Custom templates default to 'organization'.| +|description|string|false|none|Description of the LLM provider template| +|openapi|string|false|none|OpenAPI specification content (JSON or YAML) for the provider, when
uploaded/pasted. Use metadata.openapiSpecUrl instead to reference the
spec by URL.| +|metadata|[LLMProviderTemplateMetadata](#schemallmprovidertemplatemetadata)|false|none|none| +|promptTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|completionTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|totalTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|remainingTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|requestModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|responseModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|resourceMappings|[LLMProviderTemplateResourceMappings](#schemallmprovidertemplateresourcemappings)|false|none|none| + +

LLMProviderTemplateListItem

+ + + + + + +```json +{ + "id": "openai", + "groupId": "openai", + "displayName": "OpenAI", + "managedBy": "wso2", + "description": "Default OpenAI template", + "createdBy": "john.doe", + "version": "v1.0", + "isLatest": true, + "enabled": true, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "createdAt": "2025-11-25T10:30:00Z", + "updatedAt": "2025-11-25T10:30:00Z", + "readOnly": false +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique handle for this specific template version.| +|groupId|string|false|read-only|Stable identifier shared by every version of a template family
(defaults to the first version's handle). Read-only.| +|displayName|string|true|none|Human-readable name for the LLM provider template| +|managedBy|string|false|none|Who manages the template ('wso2' for built-in, otherwise custom-defined).| +|description|string|false|none|none| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|version|string|false|none|Content version, matching the v. pattern (e.g. v1.0, v2.0).| +|isLatest|boolean|false|none|Whether this is the latest version of the template.| +|enabled|boolean|false|none|Whether this version is offered when creating providers.| +|logoUrl|string(uri)|false|none|URL of the provider logo| +|createdAt|string(date-time)|false|none|none| +|updatedAt|string(date-time)|false|none|none| +|readOnly|boolean|false|none|True when the artifact originated from a data-plane gateway (origin gateway_api) and is read-only in the control plane.| + +

LLMProviderTemplateListResponse

+ + + + + + +```json +{ + "count": 5, + "list": [ + { + "id": "openai", + "groupId": "openai", + "displayName": "OpenAI", + "managedBy": "wso2", + "description": "Default OpenAI template", + "createdBy": "john.doe", + "version": "v1.0", + "isLatest": true, + "enabled": true, + "logoUrl": "https://cdn.example.com/logos/openai.svg", + "createdAt": "2025-11-25T10:30:00Z", + "updatedAt": "2025-11-25T10:30:00Z", + "readOnly": false + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|none| +|list|[[LLMProviderTemplateListItem](#schemallmprovidertemplatelistitem)]|true|none|none| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

LLMAccessControl

+ + + + + + +```json +{ + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ] + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|mode|string|true|none|Access control mode| +|exceptions|[[RouteException](#schemarouteexception)]|false|none|Path exceptions to the access control mode| + +#### Enumerated Values + +|Property|Value| +|---|---| +|mode|allow_all| +|mode|deny_all| + +

RouteException

+ + + + + + +```json +{ + "path": "/chat/completions", + "methods": [ + "GET" + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|path|string|true|none|Path pattern| +|methods|[string]|true|none|HTTP methods| + +

LLMPolicy

+ + + + + + +```json +{ + "name": "budgetControl", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|none| +|version|string|true|none|Version of the policy. Only major-only version is allowed (e.g., v0, v1). Full semantic version (e.g., v1.0.0) is not accepted and will be rejected.| +|paths|[[LLMPolicyPath](#schemallmpolicypath)]|true|none|none| + +

LLMPolicyPath

+ + + + + + +```json +{ + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|path|string|true|none|none| +|methods|[string]|true|none|none| +|params|object|true|none|JSON Schema describing the parameters accepted by this policy. This itself is a JSON Schema document.| + +

OperationPolicy

+ + + + + + +```json +{ + "name": "token-based-ratelimit", + "version": "v1", + "executionCondition": "string", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|none| +|version|string|true|none|Version of the policy. Only major-only version is allowed (e.g., v0, v1). Full semantic version (e.g., v1.0.0) is not accepted and will be rejected.| +|executionCondition|string|false|none|Optional per-request CEL expression controlling whether the policy runs| +|paths|[[OperationPolicyPath](#schemaoperationpolicypath)]|true|none|none| + +

OperationPolicyPath

+ + + + + + +```json +{ + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|path|string|true|none|none| +|methods|[string]|true|none|none| +|params|object|true|none|Policy parameters| + +

LLMRateLimitingConfig

+ + + + + + +```json +{ + "providerLevel": { + "global": { + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + "consumerLevel": { + "resourceWise": { + "default": { + "request": { + "enabled": true, + "count": 50, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 100, + "reset": { + "duration": 1, + "unit": "month" + } + } + }, + "resources": [ + { + "resource": "/models", + "limit": { + "request": { + "enabled": true, + "count": 200, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 100000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + { + "resource": "/chat/completions", + "limit": { + "request": { + "enabled": true, + "count": 25, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 10, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + } + ] + } + } +} + +``` + +Rate limiting configuration for an LLM provider at provider and consumer levels. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|providerLevel|[RateLimitingScopeConfig](#schemaratelimitingscopeconfig)|false|none|Rate limiting configuration for a scope (provider or consumer). Either global or resource-wise limits can be defined.| +|consumerLevel|[RateLimitingScopeConfig](#schemaratelimitingscopeconfig)|false|none|Rate limiting configuration for a scope (provider or consumer). Either global or resource-wise limits can be defined.| + +

RateLimitingScopeConfig

+ + + + + + +```json +{ + "global": { + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 1000, + "reset": { + "duration": 2, + "unit": "week" + } + } + }, + "resourceWise": { + "default": { + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 1000, + "reset": { + "duration": 2, + "unit": "week" + } + } + }, + "resources": [ + { + "resource": "/chat/completions", + "limit": { + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 1000, + "reset": { + "duration": 2, + "unit": "week" + } + } + } + } + ] + } +} + +``` + +Rate limiting configuration for a scope (provider or consumer). Either global or resource-wise limits can be defined. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|global|[RateLimitingLimitConfig](#schemaratelimitinglimitconfig)|false|none|Limit definition with independent request/token/cost dimensions. If all dimensions are disabled (or absent), rate limiting is off for that scope.| +|resourceWise|[ResourceWiseRateLimitingConfig](#schemaresourcewiseratelimitingconfig)|false|none|Resource-specific limits with a required default limit.| + +oneOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| + +xor + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| + +

ResourceWiseRateLimitingConfig

+ + + + + + +```json +{ + "default": { + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 1000, + "reset": { + "duration": 2, + "unit": "week" + } + } + }, + "resources": [ + { + "resource": "/chat/completions", + "limit": { + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 1000, + "reset": { + "duration": 2, + "unit": "week" + } + } + } + } + ] +} + +``` + +Resource-specific limits with a required default limit. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|default|[RateLimitingLimitConfig](#schemaratelimitinglimitconfig)|true|none|Limit definition with independent request/token/cost dimensions. If all dimensions are disabled (or absent), rate limiting is off for that scope.| +|resources|[[RateLimitingResourceLimit](#schemaratelimitingresourcelimit)]|true|none|Explicit resource limits that override the default limit.| + +

RateLimitingResourceLimit

+ + + + + + +```json +{ + "resource": "/chat/completions", + "limit": { + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 1000, + "reset": { + "duration": 2, + "unit": "week" + } + } + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|resource|string|true|none|Explicit resource path to apply the limit to.| +|limit|[RateLimitingLimitConfig](#schemaratelimitinglimitconfig)|true|none|Limit definition with independent request/token/cost dimensions. If all dimensions are disabled (or absent), rate limiting is off for that scope.| + +

RateLimitResetWindow

+ + + + + + +```json +{ + "duration": 2, + "unit": "week" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|duration|integer|true|none|Reset duration for the limit window.| +|unit|string|true|none|Reset time unit for the limit window.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|unit|minute| +|unit|hour| +|unit|day| +|unit|week| +|unit|month| + +

RequestRateLimitDimension

+ + + + + + +```json +{ + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|enabled|boolean|false|none|Enable request-count based limiting.| +|count|integer|false|none|Maximum number of requests in the reset window.| +|reset|[RateLimitResetWindow](#schemaratelimitresetwindow)|false|none|none| + +

TokenRateLimitDimension

+ + + + + + +```json +{ + "enabled": true, + "count": 1000000, + "reset": { + "duration": 2, + "unit": "week" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|enabled|boolean|false|none|Enable token-count based limiting.| +|count|integer|false|none|Maximum number of tokens in the reset window.| +|reset|[RateLimitResetWindow](#schemaratelimitresetwindow)|false|none|none| + +

CostRateLimitDimension

+ + + + + + +```json +{ + "enabled": true, + "amount": 1000, + "reset": { + "duration": 2, + "unit": "week" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|enabled|boolean|false|none|Enable cost-based limiting.| +|amount|number(float)|false|none|Maximum cost in the reset window.| +|reset|[RateLimitResetWindow](#schemaratelimitresetwindow)|false|none|none| + +

RateLimitingLimitConfig

+ + + + + + +```json +{ + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 1000, + "reset": { + "duration": 2, + "unit": "week" + } + } +} + +``` + +Limit definition with independent request/token/cost dimensions. If all dimensions are disabled (or absent), rate limiting is off for that scope. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|request|[RequestRateLimitDimension](#schemarequestratelimitdimension)|false|none|none| +|token|[TokenRateLimitDimension](#schematokenratelimitdimension)|false|none|none| +|cost|[CostRateLimitDimension](#schemacostratelimitdimension)|false|none|none| + +

LLMProvider

+ + + + + + +```json +{ + "id": "wso2-openai-provider", + "displayName": "WSO2 OpenAI Provider", + "description": "Primary OpenAI provider", + "createdBy": "john.doe", + "readOnly": false, + "updatedBy": "john.doe", + "version": "v1.0", + "context": "/openai", + "vhost": "api.openai", + "template": "openai", + "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", + "modelProviders": [ + { + "id": "claude", + "displayName": "Claude", + "models": [ + { + "id": "claude-3-5-sonnet", + "displayName": "Claude 3.5 Sonnet", + "description": "High-quality reasoning model" + } + ] + } + ], + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ] + } + ] + }, + "rateLimiting": { + "providerLevel": { + "global": { + "request": { + "enabled": true, + "count": 1500, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 1000000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + "consumerLevel": { + "resourceWise": { + "default": { + "request": { + "enabled": true, + "count": 50, + "reset": { + "duration": 2, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 100, + "reset": { + "duration": 1, + "unit": "month" + } + } + }, + "resources": [ + { + "resource": "/models", + "limit": { + "request": { + "enabled": true, + "count": 200, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "token": { + "enabled": true, + "count": 100000, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + }, + { + "resource": "/chat/completions", + "limit": { + "request": { + "enabled": true, + "count": 25, + "reset": { + "duration": 1, + "unit": "week" + } + }, + "cost": { + "enabled": true, + "amount": 10, + "reset": { + "duration": 1, + "unit": "month" + } + } + } + } + ] + } + } + }, + "globalPolicies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operationPolicies": [ + { + "name": "token-based-ratelimit", + "version": "v1", + "executionCondition": "string", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "policies": [ + { + "name": "budgetControl", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "security": { + "enabled": true, + "apiKey": { + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" + } + }, + "associatedGateways": [ + { + "id": "prod-eu", + "configurations": { + "host": "prod-eu.platform-gw.local" + } + }, + { + "id": "prod-us", + "configurations": { + "host": "prod-us.platform-gw.local" + } + } + ], + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique handle for the provider| +|displayName|string|true|none|Human-readable LLM Provider name| +|description|string|false|none|Description of the LLM provider| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|readOnly|boolean|false|read-only|True if the artifact originated from a data-plane gateway (origin gateway_api) and is read-only in the control plane; false for control-plane created artifacts.| +|updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /llm-providers/{id}), omitted from list responses.| +|version|string|true|none|Semantic version of the LLM Provider| +|context|string|false|none|Base path for all REST API routes (must start with /, no trailing slash)| +|vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| +|template|string|true|none|Template name to use for this LLM Provider| +|openapi|string|false|none|OpenAPI specification (JSON or YAML) for the provider endpoint| +|modelProviders|[[LLMModelProvider](#schemallmmodelprovider)]|false|none|List of model providers and their models supported by this provider. For non-aggregator templates, only a single model provider is allowed (aggregator templates: awsbedrock, azureaifoundry).| +|upstream|[Upstream](#schemaupstream)|true|none|Upstream backend configuration with main and sandbox endpoints| +|accessControl|[LLMAccessControl](#schemallmaccesscontrol)|true|none|none| +|rateLimiting|[LLMRateLimitingConfig](#schemallmratelimitingconfig)|false|none|Rate limiting configuration for an LLM provider at provider and consumer levels.| +|globalPolicies|[[Policy](#schemapolicy)]|false|none|Global (api-level) policies applied across ALL operations as one shared scope, evaluated before operation-level policies.| +|operationPolicies|[[OperationPolicy](#schemaoperationpolicy)]|false|none|Operation-level policies scoped to specific paths/methods, evaluated after global policies.| +|policies|[[LLMPolicy](#schemallmpolicy)]|false|none|DEPRECATED - use operationPolicies. Still honoured (treated identically to operationPolicies).| +|security|[SecurityConfig](#schemasecurityconfig)|false|none|Defines security mechanisms (API key, OAuth2) applicable to the API| +|associatedGateways|[[AssociatedGateway](#schemaassociatedgateway)]|false|none|Optional list of gateways this LLM provider can be deployed to, along with per-gateway configuration overrides. This field is optional; omitting it does not change existing behaviour.| +|createdAt|string(date-time)|false|read-only|Timestamp when the resource was created| +|updatedAt|string(date-time)|false|read-only|Timestamp when the resource was last updated| + +

AssociatedGateway

+ + + + + + +```json +{ + "id": "prod-eu", + "configurations": { + "someKey": "someValue" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|Handle of the gateway this artifact can be deployed to| +|configurations|object|false|none|Per-gateway configuration overrides for this artifact. This is a free-form object; the supported keys depend on the deployed artifact type.| + +

LLMProviderListItem

+ + + + + + +```json +{ + "id": "wso2-openai-provider", + "displayName": "WSO2 OpenAI Provider", + "description": "Primary OpenAI provider", + "createdBy": "john.doe", + "readOnly": false, + "version": "v1.0", + "template": "openai", + "status": "deployed", + "createdAt": "2025-11-25T10:30:00Z", + "updatedAt": "2025-11-25T10:30:00Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|none| +|displayName|string|true|none|Human-readable name for the LLM provider| +|description|string|false|none|none| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|readOnly|boolean|false|none|True when the artifact originated from a data-plane gateway (origin gateway_api) and is read-only in the control plane.| +|version|string|false|none|none| +|template|string|false|none|none| +|status|string|false|none|none| +|createdAt|string(date-time)|false|none|none| +|updatedAt|string(date-time)|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|pending| +|status|deployed| +|status|failed| + +

LLMModelProvider

+ + + + + + +```json +{ + "id": "claude", + "displayName": "Claude", + "models": [ + { + "id": "claude-3-5-sonnet", + "displayName": "Claude 3.5 Sonnet", + "description": "High-quality reasoning model" + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique model provider identifier| +|displayName|string|true|none|Human-readable model provider name| +|models|[[LLMModel](#schemallmmodel)]|false|none|Models under this model provider| + +

LLMModel

+ + + + + + +```json +{ + "id": "claude-3-5-sonnet", + "displayName": "Claude 3.5 Sonnet", + "description": "High-quality reasoning model" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique model identifier| +|displayName|string|true|none|Human-readable model name| +|description|string|false|none|Model description| + +

LLMProviderListResponse

+ + + + + + +```json +{ + "count": 2, + "list": [ + { + "id": "wso2-openai-provider", + "displayName": "WSO2 OpenAI Provider", + "description": "Primary OpenAI provider", + "createdBy": "john.doe", + "readOnly": false, + "version": "v1.0", + "template": "openai", + "status": "deployed", + "createdAt": "2025-11-25T10:30:00Z", + "updatedAt": "2025-11-25T10:30:00Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|none| +|list|[[LLMProviderListItem](#schemallmproviderlistitem)]|true|none|none| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

LLMProxy

+ + + + + + +```json +{ + "id": "wso2-con-assistant", + "displayName": "WSO2 Con Assistant", + "description": "Customer support assistant", + "createdBy": "john.doe", + "readOnly": false, + "updatedBy": "john.doe", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "context": "/openai", + "vhost": "api.openai", + "provider": { + "id": "wso2-openai-provider", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "additionalProviders": [ + { + "id": "anthropic-provider", + "as": "anthropic-upstream", + "transformer": { + "type": "openai-to-anthropic", + "version": "v1", + "params": {} + } + } + ], + "openapi": "openapi: 3.0.3\ninfo:\n title: Proxy API\n version: v1.0\npaths: {}\n", + "globalPolicies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "operationPolicies": [ + { + "name": "token-based-ratelimit", + "version": "v1", + "executionCondition": "string", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "policies": [ + { + "name": "budgetControl", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "security": { + "enabled": true, + "apiKey": { + "enabled": true, + "key": "X-API-Key", + "valuePrefix": "Bearer", + "in": "header" + } + }, + "associatedGateways": [ + { + "id": "prod-eu" + }, + { + "id": "prod-us" + } + ], + "createdAt": "2023-10-12T10:30:00Z", + "updatedAt": "2023-10-12T10:30:00Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique handle for the proxy| +|displayName|string|true|none|Human-readable LLM proxy name| +|description|string|false|none|Description of the LLM proxy| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|readOnly|boolean|false|read-only|True if the artifact originated from a data-plane gateway (origin gateway_api) and is read-only in the control plane; false for control-plane created artifacts.| +|updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /llm-proxies/{id}), omitted from list responses.| +|version|string|true|none|Semantic version of the LLM proxy| +|projectId|string|true|none|UUID of the project this proxy belongs to| +|context|string|false|none|Base path for all REST API routes (must start with /, no trailing slash)| +|vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| +|provider|[LLMProxyProvider](#schemallmproxyprovider)|true|none|none| +|additionalProviders|[[LLMProxyAdditionalProvider](#schemallmproxyadditionalprovider)]|false|none|Optional list of additional LLM providers attached to this proxy as selectable upstreams. Policies route requests to any of these by setting the upstream name. The primary `provider` field above remains the default upstream and the FK target.| +|openapi|string|false|none|OpenAPI specification (JSON or YAML) for the proxy endpoint| +|globalPolicies|[[Policy](#schemapolicy)]|false|none|Global (api-level) policies applied across ALL operations as one shared scope, evaluated before operation-level policies.| +|operationPolicies|[[OperationPolicy](#schemaoperationpolicy)]|false|none|Operation-level policies scoped to specific paths/methods, evaluated after global policies.| +|policies|[[LLMPolicy](#schemallmpolicy)]|false|none|DEPRECATED - use operationPolicies. Still honoured (treated identically to operationPolicies).| +|security|[SecurityConfig](#schemasecurityconfig)|false|none|Defines security mechanisms (API key, OAuth2) applicable to the API| +|associatedGateways|[[AssociatedGateway](#schemaassociatedgateway)]|false|none|Optional list of gateways this LLM proxy can be deployed to, along with per-gateway configuration overrides. This field is optional; omitting it does not change existing behaviour.| +|createdAt|string(date-time)|false|read-only|Timestamp when the resource was created| +|updatedAt|string(date-time)|false|read-only|Timestamp when the resource was last updated| + +

LLMProxyListItem

+ + + + + + +```json +{ + "id": "wso2-con-assistant", + "displayName": "WSO2 Con Assistant", + "description": "Customer support assistant", + "createdBy": "john.doe", + "context": "/wso2-con-assistant", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "provider": "wso2-openai-provider", + "status": "deployed", + "createdAt": "2025-11-25T10:30:00Z", + "updatedAt": "2025-11-25T10:30:00Z", + "readOnly": false +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|none| +|displayName|string|true|none|Human-readable name for the LLM proxy| +|description|string|false|none|none| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|context|string|false|none|Context path where the proxy is exposed| +|version|string|false|none|none| +|projectId|string|false|none|UUID of the project this proxy belongs to| +|provider|string|false|none|Unique id of a deployed llm provider| +|status|string|false|none|none| +|createdAt|string(date-time)|false|none|none| +|updatedAt|string(date-time)|false|none|none| +|readOnly|boolean|false|none|True when the artifact originated from a data-plane gateway (origin gateway_api) and is read-only in the control plane.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|pending| +|status|deployed| +|status|failed| + +

LLMProxyListResponse

+ + + + + + +```json +{ + "count": 2, + "list": [ + { + "id": "wso2-con-assistant", + "displayName": "WSO2 Con Assistant", + "description": "Customer support assistant", + "createdBy": "john.doe", + "context": "/wso2-con-assistant", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "provider": "wso2-openai-provider", + "status": "deployed", + "createdAt": "2025-11-25T10:30:00Z", + "updatedAt": "2025-11-25T10:30:00Z", + "readOnly": false + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|none| +|list|[[LLMProxyListItem](#schemallmproxylistitem)]|true|none|none| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

LLMProxyProvider

+ + + + + + +```json +{ + "id": "wso2-openai-provider", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|Unique id of a deployed llm provider| +|auth|[UpstreamAuth](#schemaupstreamauth)|false|none|Authentication configuration for upstream endpoints| + +

LLMProxyAdditionalProvider

+ + + + + + +```json +{ + "id": "anthropic-provider", + "as": "anthropic-upstream", + "transformer": { + "type": "openai-to-anthropic", + "version": "v1", + "params": {} + } +} + +``` + +Additional LLM provider attached to this proxy as a selectable upstream. Policies route to it by referring to the `as` name (defaults to `id`). + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|Unique id of a deployed llm provider| +|as|string|false|none|Logical LLM Provider name used by policies to select this provider. Must be unique within the proxy. Defaults to `id` when omitted.| +|transformer|[LLMProxyTransformer](#schemallmproxytransformer)|false|none|Request/response translator applied when this provider is the selected upstream. The proxy injects the translator as a conditional policy whose execution condition matches this provider, so it runs only when the provider is selected. The provider's `as` name (defaults to `id`) is passed to the translator as its target upstream.| + +

LLMProxyTransformer

+ + + + + + +```json +{ + "type": "openai-to-anthropic", + "version": "v1", + "params": {} +} + +``` + +Request/response translator applied when this provider is the selected upstream. The proxy injects the translator as a conditional policy whose execution condition matches this provider, so it runs only when the provider is selected. The provider's `as` name (defaults to `id`) is passed to the translator as its target upstream. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|type|string|true|none|Translator policy name (for example openai-to-anthropic).| +|version|string|true|none|Major-only translator policy version (for example v1). The Gateway Controller resolves it to the installed full version.| +|params|object|false|none|Translator-specific parameters (for example model, apiVersion).| + +

CreateLLMProviderAPIKeyRequest

+ + + + + + +```json +{ + "id": "production-key", + "displayName": "Production Key", + "expiresAt": "2026-12-31T23:59:59Z", + "issuer": "api-platform-devportal", + "allowedTargets": "dev_gateway,test_gateway" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique identifier for the API key within the LLM provider. If not provided, generated from displayName.| +|displayName|string|true|none|Human-readable name for the API key| +|expiresAt|string(date-time)|false|none|Optional expiration time in ISO 8601 format| +|issuer|string¦null|false|none|Identifier of the developer portal that provisioned this API key. Null if not provided.| +|allowedTargets|string¦null|false|none|Comma-separated list of gateways this key is valid for.
Use 'ALL' to allow all targets (default).| + +

CreateLLMProviderAPIKeyResponse

+ + + + + + +```json +{ + "status": "success", + "message": "API key created and broadcasted to gateways successfully", + "id": "production-key", + "apiKey": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|true|none|Status of the operation| +|message|string|true|none|Detailed message about the operation result| +|id|string|true|none|Unique identifier of the generated key| +|apiKey|string|true|none|The generated API key value (shown only once, 64 hexadecimal characters)| + +

CreateLLMProxyAPIKeyRequest

+ + + + + + +```json +{ + "id": "production-key", + "displayName": "Production Key", + "expiresAt": "2026-12-31T23:59:59Z", + "issuer": "api-platform-devportal", + "allowedTargets": "dev_gateway,test_gateway" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique identifier for the API key within the LLM proxy. If not provided, generated from displayName.| +|displayName|string|true|none|Human-readable name for the API key| +|expiresAt|string(date-time)|false|none|Optional expiration time in ISO 8601 format| +|issuer|string¦null|false|none|Identifier of the developer portal that provisioned this API key. Null if not provided.| +|allowedTargets|string¦null|false|none|Comma-separated list of gateways this key is valid for.
Use 'ALL' to allow all targets (default).| + +

CreateLLMProxyAPIKeyResponse

+ + + + + + +```json +{ + "status": "success", + "message": "API key created and broadcasted to gateways successfully", + "id": "production-key", + "apiKey": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|true|none|Status of the operation| +|message|string|true|none|Detailed message about the operation result| +|id|string|true|none|Unique identifier of the generated key| +|apiKey|string|true|none|The generated API key value (shown only once, 64 hexadecimal characters)| + +

MCPProxy

+ + + + + + +```json +{ + "id": "weather-mcp-proxy", + "displayName": "Weather Server", + "description": "An MCP server which provides weather information", + "createdBy": "john.doe", + "readOnly": false, + "updatedBy": "john.doe", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "context": "/", + "vhost": "mcp.gw.com", + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } + } + }, + "mcpSpecVersion": "2025-06-18", + "policies": [ + { + "executionCondition": "request.header.x-custom == 'enabled'", + "name": "SET_HEADER", + "params": { + "key": "MyHeader", + "value": "MyValue" + }, + "version": "v1" + } + ], + "kind": "Mcp", + "capabilities": { + "tools": [ + {} + ], + "resources": [ + {} + ], + "prompts": [ + {} + ] + }, + "associatedGateways": [ + { + "id": "prod-eu" + }, + { + "id": "prod-us" + } + ], + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique handle for the proxy| +|displayName|string|true|none|Human-readable MCP proxy name| +|description|string|false|none|Description of the MCP proxy| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|readOnly|boolean|false|read-only|True if the artifact originated from a data-plane gateway (origin gateway_api) and is read-only in the control plane; false for control-plane created artifacts.| +|updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /mcp-proxies/{id}), omitted from list responses.| +|version|string|true|none|Semantic version of the MCP proxy| +|projectId|string|false|none|UUID of the project this proxy belongs to| +|context|string|false|none|Base path for all REST API routes (must start with /, no trailing slash)| +|vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| +|upstream|[Upstream](#schemaupstream)|true|none|Upstream backend configuration with main and sandbox endpoints| +|mcpSpecVersion|string|false|none|MCP specification version supported by this proxy| +|policies|[[Policy](#schemapolicy)]|false|none|List of policies to be applied| +|kind|string|false|none|Kind of the API based on its communication protocol or architectural style| +|capabilities|[MCPProxyCapabilities](#schemamcpproxycapabilities)|false|none|List of capabilities supported by this proxy. This will be stored as-is and can be used in the future if we need this for governance purposes| +|associatedGateways|[[AssociatedGateway](#schemaassociatedgateway)]|false|none|Optional list of gateways this MCP proxy can be deployed to, along with per-gateway configuration overrides. This field is optional; omitting it does not change existing behaviour.| +|createdAt|string(date-time)|false|read-only|Timestamp when the resource was created| +|updatedAt|string(date-time)|false|read-only|Timestamp when the resource was last updated| + +#### Enumerated Values + +|Property|Value| +|---|---| +|mcpSpecVersion|2025-06-18| +|mcpSpecVersion|2025-11-25| + +

MCPProxyListItem

+ + + + + + +```json +{ + "id": "weather-mcp-proxy", + "displayName": "Weather Server", + "description": "An MCP server which provides weather information", + "createdBy": "john.doe", + "context": "/weather-mcp-proxy", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "status": "deployed", + "mcpSpecVersion": "2025-11-25", + "createdAt": "2025-11-25T10:30:00Z", + "updatedAt": "2025-11-25T10:30:00Z", + "readOnly": false +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|none| +|displayName|string|true|none|Human-readable name for the MCP proxy| +|description|string|false|none|none| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|context|string|false|none|Context path where the proxy is exposed| +|version|string|false|none|none| +|projectId|string|false|none|UUID of the project this proxy belongs to| +|status|string|false|none|none| +|mcpSpecVersion|string|false|none|none| +|createdAt|string(date-time)|false|none|none| +|updatedAt|string(date-time)|false|none|none| +|readOnly|boolean|false|none|True when the artifact originated from a data-plane gateway (origin gateway_api) and is read-only in the control plane.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|pending| +|status|deployed| +|status|failed| + +

MCPProxyListResponse

+ + + + + + +```json +{ + "count": 2, + "list": [ + { + "id": "weather-mcp-proxy", + "displayName": "Weather Server", + "description": "An MCP server which provides weather information", + "createdBy": "john.doe", + "context": "/weather-mcp-proxy", + "version": "v1.0", + "projectId": "550e8400-e29b-41d4-a716-446655440000", + "status": "deployed", + "mcpSpecVersion": "2025-11-25", + "createdAt": "2025-11-25T10:30:00Z", + "updatedAt": "2025-11-25T10:30:00Z", + "readOnly": false + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|none| +|list|[[MCPProxyListItem](#schemamcpproxylistitem)]|true|none|none| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

MCPServerInfoFetchRequest

+ + + + + + +```json +{ + "url": "https://mcp.server.com/mcp", + "proxyId": "my-mcp-proxy", + "auth": { + "type": "api-key", + "header": "X-API-Key", + "value": "my-api-key-value" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|url|string(uri)|false|none|Endpoint URL of the MCP server to fetch information from.
Required when proxyId is not provided. When proxyId is provided,
the URL from the stored proxy configuration is used.| +|proxyId|string|false|none|MCP proxy handle (identifier) for refresh operations. When provided,
the server fetches URL and auth from the stored proxy configuration.
Auth override is not allowed in refetch mode.| +|auth|[UpstreamAuth](#schemaupstreamauth)|false|none|Authentication configuration for the fetch request.
Only allowed when proxyId is not provided (initial creation flow).| + +

MCPServerInfoFetchResponse

+ + + + + + +```json +{ + "serverInfo": {}, + "tools": [ + {} + ], + "resources": [ + {} + ], + "prompts": [ + {} + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|serverInfo|object|false|none|none| +|tools|[object]|false|none|none| +|resources|[object]|false|none|none| +|prompts|[object]|false|none|none| + +

MCPProxyCapabilities

+ + + + + + +```json +{ + "tools": [ + {} + ], + "resources": [ + {} + ], + "prompts": [ + {} + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|tools|[object]|false|none|List of tool capabilities supported by this proxy| +|resources|[object]|false|none|List of resource capabilities supported by this proxy| +|prompts|[object]|false|none|List of prompt capabilities supported by this proxy| + +

SecretCreateRequest

+ + + + + + +```json +{ + "id": "wso2-openai-key", + "displayName": "WSO2 OpenAI API Key", + "description": "Primary API key for WSO2 OpenAI integration", + "value": "sk-xxx", + "type": "GENERIC" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Handle (slug) used in {{ secret "id" }} placeholders. Immutable after creation.| +|displayName|string|true|none|Human-readable name for the secret| +|description|string|false|none|none| +|value|string|true|none|Plaintext secret value — encrypted at rest, never returned in any response| +|type|string|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|type|GENERIC| +|type|CERTIFICATE| + +

SecretUpdateRequest

+ + + + + + +```json +{ + "id": "wso2-openai-key", + "displayName": "string", + "description": "string", + "value": "string" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Secret handle — if provided, must match the path parameter; returns 400 if they differ. The handle is immutable and cannot be changed via update.| +|displayName|string|true|none|Human-readable name for the secret| +|description|string|false|none|none| +|value|string|true|none|New plaintext secret value — re-encrypted at rest| + +

SecretResponse

+ + + + + + +```json +{ + "id": "wso2-openai-key", + "displayName": "WSO2 OpenAI API Key", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} + +``` + +Returned on create (201) and rotate (200). The plaintext value is never included. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|none| +|displayName|string|true|none|Human-readable name for the secret| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|updatedBy|string|false|read-only|User identifier of the user who last updated this resource| +|createdAt|string(date-time)|false|none|none| +|updatedAt|string(date-time)|false|none|none| + +

SecretSummary

+ + + + + + +```json +{ + "id": "wso2-openai-key", + "displayName": "WSO2 OpenAI API Key", + "description": "string", + "type": "GENERIC", + "provider": "IN_BUILT", + "status": "ACTIVE", + "hash": "hmac-sha256:b94d27b9934d3e08a52e52d7da7dabfac484efe04294e576d4b3d4c57e3f428a", + "createdBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} + +``` + +Secret metadata — never includes the plaintext value. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|none| +|displayName|string|true|none|Human-readable name for the secret| +|description|string|false|none|none| +|type|string|false|none|none| +|provider|string|false|none|none| +|status|string|false|none|none| +|hash|string|false|none|none| +|createdBy|string|false|read-only|User identifier of the user who created this resource| +|createdAt|string(date-time)|false|none|none| +|updatedAt|string(date-time)|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|type|GENERIC| +|type|CERTIFICATE| +|provider|IN_BUILT| +|status|ACTIVE| +|status|DEPRECATED| + +

SecretListResponse

+ + + + + + +```json +{ + "count": 0, + "list": [ + { + "id": "wso2-openai-key", + "displayName": "WSO2 OpenAI API Key", + "description": "string", + "type": "GENERIC", + "provider": "IN_BUILT", + "status": "ACTIVE", + "hash": "hmac-sha256:b94d27b9934d3e08a52e52d7da7dabfac484efe04294e576d4b3d4c57e3f428a", + "createdBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|Number of secrets in current response| +|list|[[SecretSummary](#schemasecretsummary)]|true|none|[Secret metadata — never includes the plaintext value.]| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

GatewayTokenListResponse

+ + + + + + +```json +{ + "count": 0, + "list": [ + { + "id": "abc12345-f678-90de-f123-456789abcdef", + "status": "active", + "createdAt": "2025-10-14T10:30:00Z", + "revokedAt": null + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|Number of tokens in current response| +|list|[[TokenInfoResponse](#schematokeninforesponse)]|true|none|List of active tokens| +|pagination|[Pagination](#schemapagination)|true|none|none| + +

CustomPolicyListResponse

+ + + + + + +```json +{ + "count": 0, + "list": [ + { + "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "organizationUuid": "bc554ded-7e40-44a7-b397-48480793ad03", + "name": "rate-limit-custom", + "version": "1.0.0", + "description": "Custom rate limiting policy", + "policyDefinition": {}, + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|count|integer|true|none|Number of custom policies in current response| +|list|[[CustomPolicyResponse](#schemacustompolicyresponse)]|true|none|List of custom policies| +|pagination|[Pagination](#schemapagination)|true|none|none| diff --git a/docs/rest-apis/platform-api/secrets.md b/docs/rest-apis/platform-api/secrets.md new file mode 100644 index 0000000000..bdf75357dc --- /dev/null +++ b/docs/rest-apis/platform-api/secrets.md @@ -0,0 +1,639 @@ +

Secrets

+ +Encrypted secret management — create, rotate, and delete organization-scoped secrets referenced via {{ secret "name" }} placeholders + +## Create a secret + + + +`POST /secrets` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/secrets \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: multipart/form-data' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Create a new encrypted secret scoped to the organization. The plaintext value is never returned. + +> Payload + +```yaml +id: wso2-openai-key +displayName: WSO2 OpenAI API Key +description: Primary API key for WSO2 OpenAI integration +value: sk-xxx +type: GENERIC + +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[SecretCreateRequest](schemas.md#schemasecretcreaterequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "id": "wso2-openai-key", + "displayName": "WSO2 OpenAI API Key", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +> 503 Response + +```json +{ + "status": "error", + "code": "SERVICE_UNAVAILABLE", + "message": "Secrets management is not configured.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Secret created successfully.|[SecretResponse](schemas.md#schemasecretresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|Service Unavailable. The secrets management feature is not configured.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## List secrets + + + +`GET /secrets` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/secrets \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Returns metadata for all secrets in the organization. The plaintext value is +never included in list or get responses. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| +|updatedAfter|query|string(date-time)|false|RFC3339 timestamp — return only secrets updated after this time. Used by GW controller for incremental polling.| + +> Example responses + +> 200 Response + +```json +{ + "count": 0, + "list": [ + { + "id": "wso2-openai-key", + "displayName": "WSO2 OpenAI API Key", + "description": "string", + "type": "GENERIC", + "provider": "IN_BUILT", + "status": "ACTIVE", + "hash": "hmac-sha256:b94d27b9934d3e08a52e52d7da7dabfac484efe04294e576d4b3d4c57e3f428a", + "createdBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +> 503 Response + +```json +{ + "status": "error", + "code": "SERVICE_UNAVAILABLE", + "message": "Secrets management is not configured.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of secret metadata|[SecretListResponse](schemas.md#schemasecretlistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|Service Unavailable. The secrets management feature is not configured.|[Error](schemas.md#schemaerror)| + +## Get a secret by handle + + + +`GET /secrets/{secretId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/secrets/{secretId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Returns metadata for a single secret. The plaintext value is never returned. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|secretId|path|string|true|The secret handle| + +> Example responses + +> 200 Response + +```json +{ + "id": "wso2-openai-key", + "displayName": "WSO2 OpenAI API Key", + "description": "string", + "type": "GENERIC", + "provider": "IN_BUILT", + "status": "ACTIVE", + "hash": "hmac-sha256:b94d27b9934d3e08a52e52d7da7dabfac484efe04294e576d4b3d4c57e3f428a", + "createdBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +> 503 Response + +```json +{ + "status": "error", + "code": "SERVICE_UNAVAILABLE", + "message": "Secrets management is not configured.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Secret metadata|[SecretSummary](schemas.md#schemasecretsummary)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|Service Unavailable. The secrets management feature is not configured.|[Error](schemas.md#schemaerror)| + +## Rotate a secret value + + + +`PUT /secrets/{secretId}` + +> Code samples + +```shell + +curl -X PUT https://localhost:9243/api/v0.9/secrets/{secretId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: multipart/form-data' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Re-encrypts and stores a new value for an existing secret. The handle is immutable +so all `{{ secret "handle" }}` placeholder references across resources remain valid +without modification. + +> Payload + +```yaml +id: wso2-openai-key +displayName: string +description: string +value: string + +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|secretId|path|string|true|The secret handle| +|body|body|[SecretUpdateRequest](schemas.md#schemasecretupdaterequest)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "id": "wso2-openai-key", + "displayName": "WSO2 OpenAI API Key", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +> 503 Response + +```json +{ + "status": "error", + "code": "SERVICE_UNAVAILABLE", + "message": "Secrets management is not configured.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Secret rotated successfully.|[SecretResponse](schemas.md#schemasecretresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|Service Unavailable. The secrets management feature is not configured.|[Error](schemas.md#schemaerror)| + +## Delete a secret + + + +`DELETE /secrets/{secretId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/secrets/{secretId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Soft-deletes a secret by marking it as DEPRECATED. Returns 409 if the secret is +still referenced by any LLM provider or API configuration. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|secretId|path|string|true|The secret handle| + +> Example responses + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "SECRET_IN_USE", + "message": "The secret is referenced by one or more active resources.", + "details": { + "references": [ + { + "type": "llm_provider", + "handle": "wso2-openai-provider", + "name": "WSO2 OpenAI Provider" + } + ] + } +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +> 503 Response + +```json +{ + "status": "error", + "code": "SERVICE_UNAVAILABLE", + "message": "Secrets management is not configured.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Secret deleted successfully|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The secret is referenced by one or more active resources.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|Service Unavailable. The secrets management feature is not configured.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/subscriptionplans.md b/docs/rest-apis/platform-api/subscriptionplans.md new file mode 100644 index 0000000000..bc67af618a --- /dev/null +++ b/docs/rest-apis/platform-api/subscriptionplans.md @@ -0,0 +1,610 @@ +

SubscriptionPlans

+ +Subscription plan management operations — rate-limit tiers that subscriptions are created against + +## Create subscription plan + + + +`POST /subscription-plans` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/subscription-plans \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Creates an organization-scoped subscription plan. + +> Payload + +```json +{ + "id": "gold", + "displayName": "Gold", + "limits": [ + { + "limitType": "REQUEST_COUNT", + "timeUnit": "HOUR", + "timeAmount": 1, + "limitCount": 10000, + "limitCountUnit": "string", + "stopOnQuotaReach": true + } + ], + "expiryTime": "2019-08-24T14:15:22Z", + "status": "ACTIVE" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[CreateSubscriptionPlanRequest](schemas.md#schemacreatesubscriptionplanrequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "id": "string", + "displayName": "string", + "limits": [ + { + "limitType": "REQUEST_COUNT", + "timeUnit": "HOUR", + "timeAmount": 1, + "limitCount": 10000, + "limitCountUnit": "string", + "stopOnQuotaReach": true + } + ], + "expiryTime": "2019-08-24T14:15:22Z", + "organizationId": "acme", + "status": "ACTIVE", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Subscription plan created successfully|[SubscriptionPlan](schemas.md#schemasubscriptionplan)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## List subscription plans + + + +`GET /subscription-plans` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/subscription-plans \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Returns subscription plans for the organization. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +> Example responses + +> 200 Response + +```json +{ + "list": [ + { + "id": "string", + "displayName": "string", + "limits": [ + { + "limitType": "REQUEST_COUNT", + "timeUnit": "HOUR", + "timeAmount": 1, + "limitCount": 10000, + "limitCountUnit": "string", + "stopOnQuotaReach": true + } + ], + "expiryTime": "2019-08-24T14:15:22Z", + "organizationId": "acme", + "status": "ACTIVE", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "count": 0, + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of subscription plans|[SubscriptionPlanListResponse](schemas.md#schemasubscriptionplanlistresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get subscription plan by ID + + + +`GET /subscription-plans/{subscriptionPlanId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/subscription-plans/{subscriptionPlanId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|subscriptionPlanId|path|string|true|none| + +> Example responses + +> 200 Response + +```json +{ + "id": "string", + "displayName": "string", + "limits": [ + { + "limitType": "REQUEST_COUNT", + "timeUnit": "HOUR", + "timeAmount": 1, + "limitCount": 10000, + "limitCountUnit": "string", + "stopOnQuotaReach": true + } + ], + "expiryTime": "2019-08-24T14:15:22Z", + "organizationId": "acme", + "status": "ACTIVE", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Subscription plan details|[SubscriptionPlan](schemas.md#schemasubscriptionplan)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Update subscription plan + + + +`PUT /subscription-plans/{subscriptionPlanId}` + +> Code samples + +```shell + +curl -X PUT https://localhost:9243/api/v0.9/subscription-plans/{subscriptionPlanId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +> Payload + +```json +{ + "id": "string", + "displayName": "string", + "limits": [ + { + "limitType": "REQUEST_COUNT", + "timeUnit": "HOUR", + "timeAmount": 1, + "limitCount": 10000, + "limitCountUnit": "string", + "stopOnQuotaReach": true + } + ], + "expiryTime": "2019-08-24T14:15:22Z", + "organizationId": "acme", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|subscriptionPlanId|path|string|true|none| +|body|body|[SubscriptionPlan](schemas.md#schemasubscriptionplan)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "id": "string", + "displayName": "string", + "limits": [ + { + "limitType": "REQUEST_COUNT", + "timeUnit": "HOUR", + "timeAmount": 1, + "limitCount": 10000, + "limitCountUnit": "string", + "stopOnQuotaReach": true + } + ], + "expiryTime": "2019-08-24T14:15:22Z", + "organizationId": "acme", + "status": "ACTIVE", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Updated subscription plan|[SubscriptionPlan](schemas.md#schemasubscriptionplan)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete subscription plan + + + +`DELETE /subscription-plans/{subscriptionPlanId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/subscription-plans/{subscriptionPlanId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|subscriptionPlanId|path|string|true|none| + +> Example responses + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Subscription plan deleted (no content)|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/subscriptions.md b/docs/rest-apis/platform-api/subscriptions.md new file mode 100644 index 0000000000..3d59223bc5 --- /dev/null +++ b/docs/rest-apis/platform-api/subscriptions.md @@ -0,0 +1,676 @@ +

Subscriptions

+ +Subscription management operations linking applications to APIs under a subscription plan + +## Create subscription + + + +`POST /subscriptions` + +> Code samples + +```shell + +curl -X POST https://localhost:9243/api/v0.9/subscriptions \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Creates a subscription for the specified artifact. +`subscriberId` identifies the unique subscriber for this artifact, allowing multiple subscribers to create subscriptions for the same artifact. + +> Payload + +```json +{ + "artifactId": "my-rest-api", + "kind": "RestApi", + "subscriberId": "user-123", + "applicationId": "my-app-handle", + "subscriptionPlanId": "gold", + "status": "ACTIVE" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[CreateSubscriptionRequest](schemas.md#schemacreatesubscriptionrequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "artifactId": "my-rest-api", + "kind": "RestApi", + "subscriberId": "string", + "applicationId": "my-app-handle", + "subscriptionToken": "string", + "subscriptionPlanId": "gold", + "subscriptionPlanName": "string", + "organizationId": "acme", + "status": "ACTIVE", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 409 Response + +```json +{ + "status": "error", + "code": "CONFLICT", + "message": "The specified resource already exists." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Subscription created successfully|[Subscription](schemas.md#schemasubscription)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string|uri|URL of the newly created resource.| + +## List subscriptions + + + +`GET /subscriptions` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/subscriptions \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Returns subscriptions filtered by artifact and/or application. +Optional query parameters artifactId, subscriberId, applicationId and status filter the list. +Supports pagination via limit and offset. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|artifactId|query|string|false|Filter by artifact ID (UUID or handle)| +|subscriberId|query|string|false|Filter by subscriber ID| +|applicationId|query|string|false|Filter by application ID| +|status|query|string|false|Filter by status (ACTIVE, INACTIVE, REVOKED)| +|limit|query|integer|false|Maximum number of items to return per page.| +|offset|query|integer|false|Zero-based index of the first item to return.| + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|status|ACTIVE| +|status|INACTIVE| +|status|REVOKED| + +> Example responses + +> 200 Response + +```json +{ + "list": [ + { + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "artifactId": "my-rest-api", + "kind": "RestApi", + "subscriberId": "string", + "applicationId": "my-app-handle", + "subscriptionToken": "string", + "subscriptionPlanId": "gold", + "subscriptionPlanName": "string", + "organizationId": "acme", + "status": "ACTIVE", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "count": 0, + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of subscriptions|[SubscriptionListResponse](schemas.md#schemasubscriptionlistresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Get subscription by ID + + + +`GET /subscriptions/{subscriptionId}` + +> Code samples + +```shell + +curl -X GET https://localhost:9243/api/v0.9/subscriptions/{subscriptionId} \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Returns a single subscription by ID (scoped to organization). +Query parameter `subscriberId` is required and must match the subscription's subscriber for access control. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|subscriptionId|path|string(uuid)|true|Subscription UUID| + +> Example responses + +> 200 Response + +```json +{ + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "artifactId": "my-rest-api", + "kind": "RestApi", + "subscriberId": "string", + "applicationId": "my-app-handle", + "subscriptionToken": "string", + "subscriptionPlanId": "gold", + "subscriptionPlanName": "string", + "organizationId": "acme", + "status": "ACTIVE", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Subscription details|[Subscription](schemas.md#schemasubscription)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Update subscription + + + +`PUT /subscriptions/{subscriptionId}` + +> Code samples + +```shell + +curl -X PUT https://localhost:9243/api/v0.9/subscriptions/{subscriptionId}?subscriberId=string \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Updates a subscription (e.g. status). +Query parameter `subscriberId` is required and must match the subscription's subscriber for access control. + +> Payload + +```json +{ + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "artifactId": "my-rest-api", + "kind": "RestApi", + "subscriberId": "string", + "applicationId": "my-app-handle", + "subscriptionToken": "string", + "subscriptionPlanId": "gold", + "subscriptionPlanName": "string", + "organizationId": "acme", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|subscriptionId|path|string(uuid)|true|Subscription UUID| +|subscriberId|query|string|true|Subscriber ID; must match the subscription's subscriberId.| +|body|body|[Subscription](schemas.md#schemasubscription)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "artifactId": "my-rest-api", + "kind": "RestApi", + "subscriberId": "string", + "applicationId": "my-app-handle", + "subscriptionToken": "string", + "subscriptionPlanId": "gold", + "subscriptionPlanName": "string", + "organizationId": "acme", + "status": "ACTIVE", + "createdBy": "john.doe", + "updatedBy": "john.doe", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Updated subscription|[Subscription](schemas.md#schemasubscription)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| + +## Delete subscription + + + +`DELETE /subscriptions/{subscriptionId}` + +> Code samples + +```shell + +curl -X DELETE https://localhost:9243/api/v0.9/subscriptions/{subscriptionId}?subscriberId=string \ + -H 'Authorization: Bearer {access_token}' \ + -H 'Accept: application/json' + +``` + +Removes the subscription for the API. +Query parameter `subscriberId` is required and must match the subscription's subscriber for access control. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|subscriptionId|path|string(uuid)|true|Subscription UUID| +|subscriberId|query|string|true|Subscriber ID; must match the subscription's subscriberId.| + +> Example responses + +> 400 Response + +```json +{ + "status": "error", + "code": "VALIDATION_FAILED", + "message": "The request failed validation.", + "errors": [ + { + "field": "spec.context", + "message": "must start with /" + } + ] +} +``` + +> 401 Response + +```json +{ + "status": "error", + "code": "UNAUTHORIZED", + "message": "Authorization header is required, or the token is invalid or expired." +} +``` + +> 403 Response + +```json +{ + "status": "error", + "code": "FORBIDDEN", + "message": "You do not have permission to perform this action." +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "NOT_FOUND", + "message": "The specified resource does not exist." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_ERROR", + "message": "An unexpected error occurred.", + "trackingId": "4f1c6f2e-8a4b-4c93-b1de-9f2f6f0c2a11" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Subscription deleted (no content)|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/platform-api/Makefile b/platform-api/Makefile index f9f1644d9c..26bcb8104e 100644 --- a/platform-api/Makefile +++ b/platform-api/Makefile @@ -34,7 +34,12 @@ MSSQL_PASSWORD ?= Strong!Passw0rd # On Apple Silicon, override with: MSSQL_IMAGE=mcr.microsoft.com/azure-sql-edge:latest MSSQL_IMAGE ?= mcr.microsoft.com/mssql/server:2022-latest -.PHONY: help build test push build-and-push-multiarch it it-sqlite it-postgres it-sqlserver it-all-dbs +# Doc generation +REPO_ROOT := $(shell git rev-parse --show-toplevel) +APIDOCS_TOOLS_DIR := $(REPO_ROOT)/tools/apidocs +DOCS_OUT_DIR := $(REPO_ROOT)/docs/rest-apis/platform-api + +.PHONY: help build test push build-and-push-multiarch it it-sqlite it-postgres it-sqlserver it-all-dbs generate generate-apidocs help: ## Show this help message @echo 'Platform API Build System (Version: $(VERSION))' @@ -105,6 +110,17 @@ generate: ## Generate API server code from OpenAPI spec @echo "Generating API models for the eventgateway plugin spec..." @go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.5.1 --config=oapi-codegen-eventgateway.yaml plugins/eventgateway/openapi.yaml +generate-apidocs: ## Generate REST API docs (Markdown) from the Platform API OpenAPI spec + @command -v widdershins >/dev/null 2>&1 || { echo "widdershins not found, installing..."; npm install -g widdershins; } + @echo "Generating REST API docs..." + @widdershins resources/openapi.yaml -o /tmp/platform-api-openapi.md \ + --omitHeader --summary \ + --language_tabs 'shell:Shell:curl' \ + --user_templates $(APIDOCS_TOOLS_DIR)/widdershins_templates/ + @bash $(APIDOCS_TOOLS_DIR)/split-openapi.sh /tmp/platform-api-openapi.md $(DOCS_OUT_DIR) + @rm -f /tmp/platform-api-openapi.md + @echo "REST API docs generated at $(DOCS_OUT_DIR)" + push: ## Push Docker image to registry @echo "Pushing Docker images..." docker push $(IMAGE_NAME):$(VERSION) diff --git a/platform-api/resources/openapi.yaml b/platform-api/resources/openapi.yaml index 741513332f..51dcb8851d 100644 --- a/platform-api/resources/openapi.yaml +++ b/platform-api/resources/openapi.yaml @@ -8788,6 +8788,8 @@ tags: description: Gateway registration and management operations - name: Gateway Tokens description: Gateway token rotation and revocation operations + - name: Gateway Policies + description: Custom gateway policy management operations - name: LLM Providers description: LLM provider management operations - name: LLM Proxies @@ -8796,6 +8798,8 @@ tags: description: LLM provider template management operations - name: LLM Provider Deployments description: LLM provider deployment operations + - name: LLM Proxy Deployments + description: LLM proxy deployment operations - name: API Keys description: API key management operations for REST APIs and LLM Providers - name: MCP Proxies @@ -8804,5 +8808,9 @@ tags: description: MCP proxy deployment operations - name: Deployments description: Deployment operations for various artifact types + - name: SubscriptionPlans + description: Subscription plan management operations — rate-limit tiers that subscriptions are created against + - name: Subscriptions + description: Subscription management operations linking applications to APIs under a subscription plan - name: Secrets description: Encrypted secret management — create, rotate, and delete organization-scoped secrets referenced via {{ secret "name" }} placeholders \ No newline at end of file diff --git a/tools/apidocs/widdershins_templates/authentication.def b/tools/apidocs/widdershins_templates/authentication.def index f913266991..94e5472dca 100644 --- a/tools/apidocs/widdershins_templates/authentication.def +++ b/tools/apidocs/widdershins_templates/authentication.def @@ -1,3 +1,34 @@ +{{ var _schemes = (data.api.components && data.api.components.securitySchemes) || {}; }} +{{ var _reqs = data.security || []; }} +{{ var _isBasic = false; var _isBearer = false; var _scopes = []; }} +{{ for (var _r = 0; _r < _reqs.length; _r++) { + var _req = _reqs[_r]; + for (var _name in _req) { + var _sd = _schemes[_name] || {}; + if (_sd.type === 'http' && (_sd.scheme || '').toLowerCase() === 'basic') { _isBasic = true; } + if (_sd.type === 'http' && (_sd.scheme || '').toLowerCase() === 'bearer') { _isBearer = true; } + if (_sd.type === 'oauth2' || _sd.type === 'openIdConnect') { + _isBearer = true; + var _reqScopes = _req[_name] || []; + for (var _s = 0; _s < _reqScopes.length; _s++) { + if (_scopes.indexOf(_reqScopes[_s]) < 0) { _scopes.push(_reqScopes[_s]); } + } + } + } + } +}} +{{? _isBearer && !_isBasic }} + +{{??}} +{{?}} diff --git a/tools/apidocs/widdershins_templates/code_shell.dot b/tools/apidocs/widdershins_templates/code_shell.dot index 3cce32acf4..f90a73cd33 100644 --- a/tools/apidocs/widdershins_templates/code_shell.dot +++ b/tools/apidocs/widdershins_templates/code_shell.dot @@ -1,7 +1,21 @@ {{ var defined_ct = data.allHeaders.find(function(h){ return h.name === 'Content-Type'; }); }} {{ if (defined_ct && data.consumes.indexOf('application/json') >= 0) { defined_ct.exampleValues.object = 'application/json'; } }} {{ var ext = 'json'; if (defined_ct) { var ct = defined_ct.exampleValues.object; if (ct.indexOf('yaml') >= 0) ext = 'yaml'; else if (ct.indexOf('xml') >= 0) ext = 'xml'; else if (ct.indexOf('text') >= 0) ext = 'txt'; } }} +{{ var _schemes = (data.api.components && data.api.components.securitySchemes) || {}; }} +{{ var _reqs = data.operation.security ? data.operation.security : (data.api.security || []); }} +{{ var _isBasic = false; var _isBearer = false; }} +{{ for (var _r = 0; _r < _reqs.length; _r++) { + var _req = _reqs[_r]; + for (var _name in _req) { + var _sd = _schemes[_name] || {}; + if (_sd.type === 'http' && (_sd.scheme || '').toLowerCase() === 'basic') { _isBasic = true; } + if (_sd.type === 'http' && (_sd.scheme || '').toLowerCase() === 'bearer') { _isBearer = true; } + if (_sd.type === 'oauth2' || _sd.type === 'openIdConnect') { _isBearer = true; } + } + } +}} +{{ var _hdrs = data.allHeaders.filter(function(h){ return h.name.toLowerCase() !== 'authorization'; }); }} curl -X {{=data.methodUpper}} {{=data.url}}{{=data.requiredQueryString}} \ - -u {username}:{password}{{?data.allHeaders.length || data.operation.requestBody}} \{{?}} -{{~data.allHeaders :p:index}} -H '{{=p.name}}: {{=p.exampleValues.object}}'{{?index < data.allHeaders.length-1 || data.operation.requestBody}} \{{?}} +{{? _isBearer && !_isBasic }} -H 'Authorization: Bearer {access_token}'{{??}} -u {username}:{password}{{?}}{{?_hdrs.length || data.operation.requestBody}} \{{?}} +{{~_hdrs :p:index}} -H '{{=p.name}}: {{=p.exampleValues.object}}'{{?index < _hdrs.length-1 || data.operation.requestBody}} \{{?}} {{~}}{{?data.operation.requestBody}} -d @payload.{{=ext}}{{?}} From 5a9e19c73f105f55a02f028ac2969e5249604469 Mon Sep 17 00:00:00 2001 From: Thushani Jayasekera Date: Wed, 29 Jul 2026 19:09:52 +0530 Subject: [PATCH 2/7] Update API documentation for various platform endpoints --- docs/rest-apis/platform-api/README.md | 2 +- docs/rest-apis/platform-api/api-keys.md | 2 +- docs/rest-apis/platform-api/applications.md | 80 ++-- docs/rest-apis/platform-api/authentication.md | 8 +- .../platform-api/gateway-policies.md | 20 +- docs/rest-apis/platform-api/gateway-tokens.md | 16 +- docs/rest-apis/platform-api/gateways.md | 40 +- .../platform-api/llm-provider-deployments.md | 67 ++-- .../platform-api/llm-provider-templates.md | 348 ++++++++---------- docs/rest-apis/platform-api/llm-providers.md | 82 ++--- docs/rest-apis/platform-api/llm-proxies.md | 52 +-- .../platform-api/llm-proxy-deployments.md | 67 ++-- docs/rest-apis/platform-api/mcp-proxies.md | 66 ++-- .../platform-api/mcp-proxy-deployments.md | 67 ++-- docs/rest-apis/platform-api/organizations.md | 16 +- docs/rest-apis/platform-api/projects.md | 49 +-- .../platform-api/rest-api-deployments.md | 67 ++-- docs/rest-apis/platform-api/rest-apis.md | 120 +++--- docs/rest-apis/platform-api/schemas.md | 128 +++---- docs/rest-apis/platform-api/secrets.md | 58 ++- .../platform-api/subscriptionplans.md | 26 +- docs/rest-apis/platform-api/subscriptions.md | 37 +- platform-api/api/generated.go | 47 ++- platform-api/resources/openapi.yaml | 285 +++++++++++--- .../widdershins_templates/code_shell.dot | 15 +- .../widdershins_templates/operation.dot | 3 +- .../widdershins_templates/responses.def | 2 +- 27 files changed, 946 insertions(+), 824 deletions(-) diff --git a/docs/rest-apis/platform-api/README.md b/docs/rest-apis/platform-api/README.md index 6757d57ad1..b6579796b5 100644 --- a/docs/rest-apis/platform-api/README.md +++ b/docs/rest-apis/platform-api/README.md @@ -49,7 +49,7 @@ License: Apache 2.0 Example responses - +> > 200 Response ```json diff --git a/docs/rest-apis/platform-api/applications.md b/docs/rest-apis/platform-api/applications.md index cb8a7fd245..2b3a8b6987 100644 --- a/docs/rest-apis/platform-api/applications.md +++ b/docs/rest-apis/platform-api/applications.md @@ -50,7 +50,7 @@ Required scopes (the token must carry at least one of): `ap:application:create`, |body|body|[CreateApplicationRequest](schemas.md#schemacreateapplicationrequest)|true|none| > Example responses - +> > 201 Response ```json @@ -76,8 +76,8 @@ Required scopes (the token must carry at least one of): `ap:application:create`, "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -119,7 +119,7 @@ Required scopes (the token must carry at least one of): `ap:application:create`, { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -143,7 +143,7 @@ Required scopes (the token must carry at least one of): `ap:application:create`, |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ### Response Headers @@ -184,7 +184,7 @@ Required scopes (the token must carry at least one of): `ap:application:read`, ` |Name|In|Type|Required|Description| |---|---|---|---|---| -|projectId|query|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by.| +|projectId|query|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project whose resources should be returned.| |limit|query|integer|false|Maximum number of items to return per page.| |offset|query|integer|false|Zero-based index of the first item to return.| |sortBy|query|string|false|Field to sort the collection by. An unrecognized value falls back to the default sort (createdAt).| @@ -193,7 +193,7 @@ Required scopes (the token must carry at least one of): `ap:application:read`, ` #### Detailed descriptions -**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by. +**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project whose resources should be returned. #### Enumerated Values @@ -205,7 +205,7 @@ Required scopes (the token must carry at least one of): `ap:application:read`, ` |sortOrder|desc| > Example responses - +> > 200 Response ```json @@ -311,7 +311,7 @@ Required scopes (the token must carry at least one of): `ap:application:read`, ` **applicationId**: **Application ID** consisting of the **handle** of the application. > Example responses - +> > 200 Response ```json @@ -337,8 +337,8 @@ Required scopes (the token must carry at least one of): `ap:application:read`, ` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -438,7 +438,7 @@ Required scopes (the token must carry at least one of): `ap:application:update`, **applicationId**: **Application ID** consisting of the **handle** of the application. > Example responses - +> > 200 Response ```json @@ -464,8 +464,8 @@ Required scopes (the token must carry at least one of): `ap:application:update`, "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -507,7 +507,7 @@ Required scopes (the token must carry at least one of): `ap:application:update`, { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -531,7 +531,7 @@ Required scopes (the token must carry at least one of): `ap:application:update`, |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ## Delete application @@ -572,7 +572,7 @@ Required scopes (the token must carry at least one of): `ap:application:delete`, **applicationId**: **Application ID** consisting of the **handle** of the application. > Example responses - +> > 400 Response ```json @@ -582,8 +582,8 @@ Required scopes (the token must carry at least one of): `ap:application:delete`, "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -681,7 +681,7 @@ Required scopes (the token must carry at least one of): `ap:application:api_key: **applicationId**: **Application ID** consisting of the **handle** of the application. > Example responses - +> > 200 Response ```json @@ -718,8 +718,8 @@ Required scopes (the token must carry at least one of): `ap:application:api_key: "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -833,7 +833,7 @@ Required scopes (the token must carry at least one of): `ap:application:api_key: **applicationId**: **Application ID** consisting of the **handle** of the application. > Example responses - +> > 200 Response ```json @@ -870,8 +870,8 @@ Required scopes (the token must carry at least one of): `ap:application:api_key: "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -973,7 +973,7 @@ Required scopes (the token must carry at least one of): `ap:application:api_key: **entityID**: **Entity ID** of the artifact associated with the API key mapping. > Example responses - +> > 400 Response ```json @@ -983,8 +983,8 @@ Required scopes (the token must carry at least one of): `ap:application:api_key: "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1082,7 +1082,7 @@ Required scopes (the token must carry at least one of): `ap:application:associat **applicationId**: **Application ID** consisting of the **handle** of the application. > Example responses - +> > 200 Response ```json @@ -1115,8 +1115,8 @@ Required scopes (the token must carry at least one of): `ap:application:associat "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1217,7 +1217,7 @@ Required scopes (the token must carry at least one of): `ap:application:associat **applicationId**: **Application ID** consisting of the **handle** of the application. > Example responses - +> > 200 Response ```json @@ -1250,8 +1250,8 @@ Required scopes (the token must carry at least one of): `ap:application:associat "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1350,7 +1350,7 @@ Required scopes (the token must carry at least one of): `ap:application:associat **associationId**: **Association ID** consisting of the **handle** or **UUID** of the association target. > Example responses - +> > 400 Response ```json @@ -1360,8 +1360,8 @@ Required scopes (the token must carry at least one of): `ap:application:associat "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1462,7 +1462,7 @@ Required scopes (the token must carry at least one of): `ap:application:associat **associationId**: **Association ID** consisting of the **handle** or **UUID** of the association target. > Example responses - +> > 200 Response ```json @@ -1499,8 +1499,8 @@ Required scopes (the token must carry at least one of): `ap:application:associat "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } diff --git a/docs/rest-apis/platform-api/authentication.md b/docs/rest-apis/platform-api/authentication.md index f26ecddd4c..e44ccea978 100644 --- a/docs/rest-apis/platform-api/authentication.md +++ b/docs/rest-apis/platform-api/authentication.md @@ -236,7 +236,13 @@ which part of a credential was wrong. The complete set of scopes defined by this API, as declared in the OpenAPI `OAuth2Security` scheme. - OAuth2, flow: `clientCredentials` -- Token URL (file mode): [https://localhost:9243/oauth2/token](https://localhost:9243/oauth2/token) +- Token URL declared in the spec: `https://localhost:9243/oauth2/token` + +The declared token URL describes the `clientCredentials` flow an external IDP is expected to expose, +and is what generated clients target in `idp` mode — the Platform API does not serve it itself. +In `file` mode there is no client-credentials endpoint; obtain a token from the username/password +login endpoint described under [Obtaining a token](#obtaining-a-token) +(`POST /api/portal/v0.9/auth/login`) instead. |Scope|Scope Description| |---|---| diff --git a/docs/rest-apis/platform-api/gateway-policies.md b/docs/rest-apis/platform-api/gateway-policies.md index f198dd96fa..efdaef8c12 100644 --- a/docs/rest-apis/platform-api/gateway-policies.md +++ b/docs/rest-apis/platform-api/gateway-policies.md @@ -37,7 +37,7 @@ Required scopes (the token must carry at least one of): `ap:gateway_custom_polic |offset|query|integer|false|Zero-based index of the first item to return.| > Example responses - +> > 200 Response ```json @@ -135,7 +135,7 @@ Required scopes (the token must carry at least one of): `ap:gateway_custom_polic |policyVersion|query|string|true|Version of the custom policy in MAJOR.MINOR.PATCH format| > Example responses - +> > 200 Response ```json @@ -160,8 +160,8 @@ Required scopes (the token must carry at least one of): `ap:gateway_custom_polic "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -203,7 +203,7 @@ Required scopes (the token must carry at least one of): `ap:gateway_custom_polic { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -237,7 +237,7 @@ Required scopes (the token must carry at least one of): `ap:gateway_custom_polic |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Policy is not a custom policy or manifest is unavailable|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| @@ -276,7 +276,7 @@ Required scopes (the token must carry at least one of): `ap:gateway_custom_polic |version|path|string|true|Version of the custom policy (e.g. "1.0.0")| > Example responses - +> > 200 Response ```json @@ -367,7 +367,7 @@ Required scopes (the token must carry at least one of): `ap:gateway_custom_polic |version|path|string|true|Version of the custom policy (e.g. "1.0.0")| > Example responses - +> > 401 Response ```json @@ -404,7 +404,7 @@ Required scopes (the token must carry at least one of): `ap:gateway_custom_polic { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -427,5 +427,5 @@ Required scopes (the token must carry at least one of): `ap:gateway_custom_polic |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/gateway-tokens.md b/docs/rest-apis/platform-api/gateway-tokens.md index 5c4b72e542..242531e5b1 100644 --- a/docs/rest-apis/platform-api/gateway-tokens.md +++ b/docs/rest-apis/platform-api/gateway-tokens.md @@ -43,7 +43,7 @@ Required scopes (the token must carry at least one of): `ap:gateway:token:read`, **gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway. > Example responses - +> > 200 Response ```json @@ -144,13 +144,13 @@ Required scopes (the token must carry at least one of): `ap:gateway:token:create **gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway. > Example responses - +> > 201 Response ```json { "id": "def45678-g901-23hi-j456-789012klmnop", - "token": "nM8pQ1rT4vW7yZ0bC3eF6hI9kL2nO5qR8tU1wX4zA7cE0fH3jK6mP9sV2yB5dG8i", + "token": "REDACTED_TOKEN", "createdAt": "2025-10-15T14:20:00Z", "message": "New token generated successfully. Old token remains active until revoked." } @@ -165,8 +165,8 @@ Required scopes (the token must carry at least one of): `ap:gateway:token:create "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -272,7 +272,7 @@ Required scopes (the token must carry at least one of): `ap:gateway:token:delete **tokenId**: **Token ID** consisting of the **UUID** of the Token. > Example responses - +> > 200 Response ```json @@ -290,8 +290,8 @@ Required scopes (the token must carry at least one of): `ap:gateway:token:delete "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } diff --git a/docs/rest-apis/platform-api/gateways.md b/docs/rest-apis/platform-api/gateways.md index db4501ea6f..41e1d4de8f 100644 --- a/docs/rest-apis/platform-api/gateways.md +++ b/docs/rest-apis/platform-api/gateways.md @@ -60,7 +60,7 @@ Required scopes (the token must carry at least one of): `ap:gateway:create`, `ap |body|body|[CreateGatewayRequest](schemas.md#schemacreategatewayrequest)|true|Gateway registration details| > Example responses - +> > 201 Response ```json @@ -97,8 +97,8 @@ Required scopes (the token must carry at least one of): `ap:gateway:create`, `ap "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -140,7 +140,7 @@ Required scopes (the token must carry at least one of): `ap:gateway:create`, `ap { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -164,7 +164,7 @@ Required scopes (the token must carry at least one of): `ap:gateway:create`, `ap |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ### Response Headers @@ -221,7 +221,7 @@ Required scopes (the token must carry at least one of): `ap:gateway:read`, `ap:g |sortOrder|desc| > Example responses - +> > 200 Response ```json @@ -268,8 +268,8 @@ Required scopes (the token must carry at least one of): `ap:gateway:read`, `ap:g "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -344,7 +344,7 @@ Required scopes (the token must carry at least one of): `ap:gateway:read`, `ap:g **gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway. > Example responses - +> > 200 Response ```json @@ -381,8 +381,8 @@ Required scopes (the token must carry at least one of): `ap:gateway:read`, `ap:g "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -495,7 +495,7 @@ Required scopes (the token must carry at least one of): `ap:gateway:update`, `ap **gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway. > Example responses - +> > 200 Response ```json @@ -532,8 +532,8 @@ Required scopes (the token must carry at least one of): `ap:gateway:update`, `ap "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -631,7 +631,7 @@ Required scopes (the token must carry at least one of): `ap:gateway:delete`, `ap **gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway. > Example responses - +> > 400 Response ```json @@ -641,8 +641,8 @@ Required scopes (the token must carry at least one of): `ap:gateway:delete`, `ap "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -684,7 +684,7 @@ Required scopes (the token must carry at least one of): `ap:gateway:delete`, `ap { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -708,7 +708,7 @@ Required scopes (the token must carry at least one of): `ap:gateway:delete`, `ap |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ## Get gateway policy manifest @@ -751,7 +751,7 @@ Required scopes (the token must carry at least one of): `ap:gateway:manifest:rea **gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway. > Example responses - +> > 200 Response ```json diff --git a/docs/rest-apis/platform-api/llm-provider-deployments.md b/docs/rest-apis/platform-api/llm-provider-deployments.md index 5fb7858d61..61df59102b 100644 --- a/docs/rest-apis/platform-api/llm-provider-deployments.md +++ b/docs/rest-apis/platform-api/llm-provider-deployments.md @@ -53,19 +53,18 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym |body|body|[DeployRequest](schemas.md#schemadeployrequest)|true|Deployment request with gateway ID, base reference, and metadata| > Example responses - -> 201 Response +> +> Asynchronous operation accepted; poll the deployment until status becomes DEPLOYED or FAILED. ```json { "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", "name": "v1.0-production", "gatewayId": "prod-gateway-01", - "status": "DEPLOYED", + "status": "DEPLOYING", "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", "metadata": {}, "createdAt": "2019-08-24T14:15:22Z", - "statusReason": "string", "updatedAt": "2019-08-24T14:15:22Z" } ``` @@ -79,8 +78,8 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -161,7 +160,7 @@ curl -X GET https://localhost:9243/api/v0.9/llm-providers/{llmProviderId}/deploy ``` Retrieves all deployment artifacts for a specific LLM provider. The providerId parameter is the -LLM provider handle (identifier), not the UUID. Supports filtering by gateway UUID and deployment status. +LLM provider handle (identifier), not the UUID. Supports filtering by gateway handle and deployment status. Access is validated against the organization in the JWT token. ### Authentication @@ -178,14 +177,14 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym |Name|In|Type|Required|Description| |---|---|---|---|---| |llmProviderId|path|string|true|Unique identifier of the LLM provider| -|gatewayId|query|string|false|**Gateway ID** consisting of the **UUID** of the Gateway to filter status by.| +|gatewayId|query|string|false|**Gateway ID** (handle — unique slug identifier) of the Gateway to filter deployments by.| |status|query|string|false|Filter deployments by status (DEPLOYED, UNDEPLOYED, DEPLOYING, UNDEPLOYING, FAILED, or ARCHIVED)| |limit|query|integer|false|Maximum number of items to return per page.| |offset|query|integer|false|Zero-based index of the first item to return.| #### Detailed descriptions -**gatewayId**: **Gateway ID** consisting of the **UUID** of the Gateway to filter status by. +**gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway to filter deployments by. #### Enumerated Values @@ -199,7 +198,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym |status|ARCHIVED| > Example responses - +> > 200 Response ```json @@ -235,8 +234,8 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -319,7 +318,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym |deploymentId|path|string(uuid)|true|The UUID of the deployment| > Example responses - +> > 200 Response ```json @@ -412,7 +411,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym |deploymentId|path|string(uuid)|true|The UUID of the deployment| > Example responses - +> > 400 Response ```json @@ -422,8 +421,8 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -464,8 +463,8 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym ```json { "status": "error", - "code": "CONFLICT", - "message": "The specified resource already exists." + "code": "DEPLOYMENT_ACTIVE", + "message": "Cannot delete an active deployment - undeploy it first." } ``` @@ -489,7 +488,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The deployment is still active and must be undeployed before deletion.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ## Undeploy LLM provider deployment from gateway @@ -533,19 +532,18 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym |gatewayId|query|string|true|UUID of the gateway (validated against deployment's bound gateway)| > Example responses - -> 200 Response +> +> Asynchronous operation accepted; poll the deployment until status becomes UNDEPLOYED or FAILED. ```json { "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", "name": "v1.0-production", "gatewayId": "prod-gateway-01", - "status": "DEPLOYED", + "status": "UNDEPLOYING", "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", "metadata": {}, "createdAt": "2019-08-24T14:15:22Z", - "statusReason": "string", "updatedAt": "2019-08-24T14:15:22Z" } ``` @@ -559,8 +557,8 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -602,7 +600,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -626,7 +624,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ## Restore a previous LLM provider deployment @@ -670,19 +668,18 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym |gatewayId|query|string|true|UUID of the gateway (validated against deployment's bound gateway)| > Example responses - -> 200 Response +> +> Asynchronous operation accepted; poll the deployment until status becomes DEPLOYED or FAILED. ```json { "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", "name": "v1.0-production", "gatewayId": "prod-gateway-01", - "status": "DEPLOYED", + "status": "DEPLOYING", "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", "metadata": {}, "createdAt": "2019-08-24T14:15:22Z", - "statusReason": "string", "updatedAt": "2019-08-24T14:15:22Z" } ``` @@ -696,8 +693,8 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -739,7 +736,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -763,5 +760,5 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:deploym |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/llm-provider-templates.md b/docs/rest-apis/platform-api/llm-provider-templates.md index 9296538748..ce41b77e41 100644 --- a/docs/rest-apis/platform-api/llm-provider-templates.md +++ b/docs/rest-apis/platform-api/llm-provider-templates.md @@ -51,23 +51,23 @@ identified via the JWT token. }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "resourceMappings": { "resources": [ @@ -79,23 +79,23 @@ identified via the JWT token. }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" } } ] @@ -119,7 +119,7 @@ Required scopes (the token must carry at least one of): `ap:llm_template:create` |body|body|[LLMProviderTemplate](schemas.md#schemallmprovidertemplate)|true|The template family to create (starts at v1.0).| > Example responses - +> > 201 Response ```json @@ -152,23 +152,23 @@ Required scopes (the token must carry at least one of): `ap:llm_template:create` }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "resourceMappings": { "resources": [ @@ -180,23 +180,23 @@ Required scopes (the token must carry at least one of): `ap:llm_template:create` }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" } } ] @@ -215,8 +215,8 @@ Required scopes (the token must carry at least one of): `ap:llm_template:create` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -258,7 +258,7 @@ Required scopes (the token must carry at least one of): `ap:llm_template:create` { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -282,7 +282,7 @@ Required scopes (the token must carry at least one of): `ap:llm_template:create` |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ### Response Headers @@ -337,7 +337,7 @@ Required scopes (the token must carry at least one of): `ap:llm_template:read`, |offset|query|integer|false|Zero-based index of the first item to return.| > Example responses - +> > 200 Response ```json @@ -377,8 +377,8 @@ Required scopes (the token must carry at least one of): `ap:llm_template:read`, "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -435,6 +435,50 @@ Required scopes (the token must carry at least one of): `ap:llm_template:read`, |location|header| |location|queryParam| |location|pathParam| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| ## Create a new version by copying an existing one @@ -495,23 +539,23 @@ its own `group_id` and starts at v1.0. }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "resourceMappings": { "resources": [ @@ -523,23 +567,23 @@ its own `group_id` and starts at v1.0. }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" } } ] @@ -566,90 +610,22 @@ Required scopes (the token must carry at least one of): `ap:llm_template:create` |body|body|[CreateLLMProviderTemplateVersionRequest](schemas.md#schemacreatellmprovidertemplateversionrequest)|false|Optional overrides applied on top of the copied config. Any field present replaces the value cloned from the source version.| > Example responses - -> 201 Response +> +> New version created successfully ```json { - "id": "openai", + "id": "openai-v4-0", "groupId": "openai", "displayName": "OpenAI", - "managedBy": "wso2", + "managedBy": "organization", "description": "Default OpenAI template", "createdBy": "john.doe", "updatedBy": "john.doe", "readOnly": false, - "version": "v1.0", + "version": "v4.0", "isLatest": true, - "enabled": true, - "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", - "metadata": { - "endpointUrl": "https://api.openai.com", - "auth": { - "type": "bearer", - "header": "Authorization", - "valuePrefix": "Bearer " - }, - "logoUrl": "https://cdn.example.com/logos/openai.svg", - "openapiSpecUrl": "https://api.openai.com/openapi.json" - }, - "promptTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" - }, - "completionTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" - }, - "totalTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" - }, - "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" - }, - "requestModel": { - "location": "payload", - "identifier": "$.usage.inputTokens" - }, - "responseModel": { - "location": "payload", - "identifier": "$.usage.inputTokens" - }, - "resourceMappings": { - "resources": [ - { - "resource": "/responses", - "promptTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" - }, - "completionTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" - }, - "totalTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" - }, - "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" - }, - "requestModel": { - "location": "payload", - "identifier": "$.usage.inputTokens" - }, - "responseModel": { - "location": "payload", - "identifier": "$.usage.inputTokens" - } - } - ] - }, - "createdAt": "2023-10-12T10:30:00Z", - "updatedAt": "2023-10-12T10:30:00Z" + "enabled": true } ``` @@ -662,8 +638,8 @@ Required scopes (the token must carry at least one of): `ap:llm_template:create` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -705,7 +681,7 @@ Required scopes (the token must carry at least one of): `ap:llm_template:create` { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -729,7 +705,7 @@ Required scopes (the token must carry at least one of): `ap:llm_template:create` |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ### Response Headers @@ -772,7 +748,7 @@ Required scopes (the token must carry at least one of): `ap:llm_template:read`, |llmProviderTemplateId|path|string|true|Unique handle of the template.| > Example responses - +> > 200 Response ```json @@ -805,23 +781,23 @@ Required scopes (the token must carry at least one of): `ap:llm_template:read`, }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "resourceMappings": { "resources": [ @@ -833,23 +809,23 @@ Required scopes (the token must carry at least one of): `ap:llm_template:read`, }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" } } ] @@ -947,23 +923,23 @@ version of the template. }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "resourceMappings": { "resources": [ @@ -975,23 +951,23 @@ version of the template. }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" } } ] @@ -1016,7 +992,7 @@ Required scopes (the token must carry at least one of): `ap:llm_template:update` |body|body|[LLMProviderTemplate](schemas.md#schemallmprovidertemplate)|true|none| > Example responses - +> > 200 Response ```json @@ -1049,23 +1025,23 @@ Required scopes (the token must carry at least one of): `ap:llm_template:update` }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "resourceMappings": { "resources": [ @@ -1077,23 +1053,23 @@ Required scopes (the token must carry at least one of): `ap:llm_template:update` }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" } } ] @@ -1112,8 +1088,8 @@ Required scopes (the token must carry at least one of): `ap:llm_template:update` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1197,7 +1173,7 @@ by a provider is rejected (409). ```json { - "enabled": false + "enabled": true } ``` @@ -1216,10 +1192,10 @@ Required scopes (the token must carry at least one of): `ap:llm_template:update` |---|---|---|---|---| |llmProviderTemplateId|path|string|true|Unique handle of the template version.| |body|body|object|true|none| -|» enabled|body|boolean|true|none| +|» enabled|body|boolean|true|Set to true to enable this template version, false to disable it.| > Example responses - +> > 200 Response ```json @@ -1252,23 +1228,23 @@ Required scopes (the token must carry at least one of): `ap:llm_template:update` }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "resourceMappings": { "resources": [ @@ -1280,23 +1256,23 @@ Required scopes (the token must carry at least one of): `ap:llm_template:update` }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" } } ] @@ -1315,8 +1291,8 @@ Required scopes (the token must carry at least one of): `ap:llm_template:update` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1358,7 +1334,7 @@ Required scopes (the token must carry at least one of): `ap:llm_template:update` { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -1382,7 +1358,7 @@ Required scopes (the token must carry at least one of): `ap:llm_template:update` |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ## Delete a template version @@ -1421,7 +1397,7 @@ Required scopes (the token must carry at least one of): `ap:llm_template:delete` |llmProviderTemplateId|path|string|true|Unique handle of the template version.| > Example responses - +> > 400 Response ```json @@ -1431,8 +1407,8 @@ Required scopes (the token must carry at least one of): `ap:llm_template:delete` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1474,7 +1450,7 @@ Required scopes (the token must carry at least one of): `ap:llm_template:delete` { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -1498,5 +1474,5 @@ Required scopes (the token must carry at least one of): `ap:llm_template:delete` |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/llm-providers.md b/docs/rest-apis/platform-api/llm-providers.md index c24fdd07e4..0436c299af 100644 --- a/docs/rest-apis/platform-api/llm-providers.md +++ b/docs/rest-apis/platform-api/llm-providers.md @@ -31,7 +31,7 @@ Deploy a new LLM provider configuration. "description": "Primary OpenAI provider", "version": "v1.0", "context": "/openai", - "vhost": "api.openai", + "vhost": "api.openai.com", "template": "openai", "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", "modelProviders": [ @@ -50,7 +50,6 @@ Deploy a new LLM provider configuration. "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -59,7 +58,6 @@ Deploy a new LLM provider configuration. }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -250,7 +248,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:create` |body|body|[LLMProvider](schemas.md#schemallmprovider)|true|none| > Example responses - +> > 201 Response ```json @@ -263,7 +261,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:create` "updatedBy": "john.doe", "version": "v1.0", "context": "/openai", - "vhost": "api.openai", + "vhost": "api.openai.com", "template": "openai", "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", "modelProviders": [ @@ -282,18 +280,18 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:create` "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } } }, @@ -475,8 +473,8 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:create` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -508,7 +506,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:create` { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -531,7 +529,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:create` |400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ### Response Headers @@ -575,7 +573,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:read`, |offset|query|integer|false|Zero-based index of the first item to return.| > Example responses - +> > 200 Response ```json @@ -666,7 +664,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:read`, |llmProviderId|path|string|true|Unique identifier of the LLM provider| > Example responses - +> > 200 Response ```json @@ -679,7 +677,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:read`, "updatedBy": "john.doe", "version": "v1.0", "context": "/openai", - "vhost": "api.openai", + "vhost": "api.openai.com", "template": "openai", "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", "modelProviders": [ @@ -698,18 +696,18 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:read`, "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } } }, @@ -951,7 +949,7 @@ Update the configuration of an existing LLM provider. "description": "Primary OpenAI provider", "version": "v1.0", "context": "/openai", - "vhost": "api.openai", + "vhost": "api.openai.com", "template": "openai", "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", "modelProviders": [ @@ -970,7 +968,6 @@ Update the configuration of an existing LLM provider. "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -979,7 +976,6 @@ Update the configuration of an existing LLM provider. }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -1171,7 +1167,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:update` |body|body|[LLMProvider](schemas.md#schemallmprovider)|true|none| > Example responses - +> > 200 Response ```json @@ -1184,7 +1180,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:update` "updatedBy": "john.doe", "version": "v1.0", "context": "/openai", - "vhost": "api.openai", + "vhost": "api.openai.com", "template": "openai", "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", "modelProviders": [ @@ -1203,18 +1199,18 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:update` "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } } }, @@ -1396,8 +1392,8 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:update` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1489,7 +1485,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:delete` |llmProviderId|path|string|true|Unique identifier of the LLM provider| > Example responses - +> > 400 Response ```json @@ -1499,8 +1495,8 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:delete` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1594,7 +1590,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment |offset|query|integer|false|Zero-based index of the first item to return.| > Example responses - +> > 200 Response ```json @@ -1633,8 +1629,8 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1732,7 +1728,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:api_key |body|body|[CreateLLMProviderAPIKeyRequest](schemas.md#schemacreatellmproviderapikeyrequest)|true|API key creation details| > Example responses - +> > 201 Response ```json @@ -1740,7 +1736,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:api_key "status": "success", "message": "API key created and broadcasted to gateways successfully", "id": "production-key", - "apiKey": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456" + "apiKey": "REDACTED_API_KEY" } ``` @@ -1753,8 +1749,8 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:api_key "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1855,7 +1851,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:api_key |offset|query|integer|false|Zero-based index of the first item to return.| > Example responses - +> > 200 Response ```json @@ -1958,7 +1954,7 @@ Required scopes (the token must carry at least one of): `ap:llm_provider:api_key |apiKeyId|path|string|true|Name of the API key to delete| > Example responses - +> > 401 Response ```json diff --git a/docs/rest-apis/platform-api/llm-proxies.md b/docs/rest-apis/platform-api/llm-proxies.md index c930f1b269..cab39797a5 100644 --- a/docs/rest-apis/platform-api/llm-proxies.md +++ b/docs/rest-apis/platform-api/llm-proxies.md @@ -32,7 +32,7 @@ Deploy a new LLM proxy configuration. "version": "v1.0", "projectId": "550e8400-e29b-41d4-a716-446655440000", "context": "/openai", - "vhost": "api.openai", + "vhost": "api.openai.com", "provider": { "id": "wso2-openai-provider", "auth": { @@ -131,7 +131,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:create`, ` |body|body|[LLMProxy](schemas.md#schemallmproxy)|true|none| > Example responses - +> > 201 Response ```json @@ -145,7 +145,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:create`, ` "version": "v1.0", "projectId": "550e8400-e29b-41d4-a716-446655440000", "context": "/openai", - "vhost": "api.openai", + "vhost": "api.openai.com", "provider": { "id": "wso2-openai-provider", "auth": { @@ -238,8 +238,8 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:create`, ` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -281,7 +281,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:create`, ` { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -305,7 +305,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:create`, ` |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ### Response Headers @@ -345,16 +345,16 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:read`, `ap |Name|In|Type|Required|Description| |---|---|---|---|---| -|projectId|query|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by.| +|projectId|query|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project whose resources should be returned.| |limit|query|integer|false|Maximum number of items to return per page.| |offset|query|integer|false|Zero-based index of the first item to return.| #### Detailed descriptions -**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by. +**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project whose resources should be returned. > Example responses - +> > 200 Response ```json @@ -447,7 +447,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:read`, `ap |llmProxyId|path|string|true|Unique identifier of the LLM proxy| > Example responses - +> > 200 Response ```json @@ -461,7 +461,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:read`, `ap "version": "v1.0", "projectId": "550e8400-e29b-41d4-a716-446655440000", "context": "/openai", - "vhost": "api.openai", + "vhost": "api.openai.com", "provider": { "id": "wso2-openai-provider", "auth": { @@ -615,7 +615,7 @@ Update the configuration of an existing LLM proxy. "version": "v1.0", "projectId": "550e8400-e29b-41d4-a716-446655440000", "context": "/openai", - "vhost": "api.openai", + "vhost": "api.openai.com", "provider": { "id": "wso2-openai-provider", "auth": { @@ -715,7 +715,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:update`, ` |body|body|[LLMProxy](schemas.md#schemallmproxy)|true|none| > Example responses - +> > 200 Response ```json @@ -729,7 +729,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:update`, ` "version": "v1.0", "projectId": "550e8400-e29b-41d4-a716-446655440000", "context": "/openai", - "vhost": "api.openai", + "vhost": "api.openai.com", "provider": { "id": "wso2-openai-provider", "auth": { @@ -822,8 +822,8 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:update`, ` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -915,7 +915,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:delete`, ` |llmProxyId|path|string|true|Unique identifier of the LLM proxy| > Example responses - +> > 400 Response ```json @@ -925,8 +925,8 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:delete`, ` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1035,7 +1035,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:api_key:cr |body|body|[CreateLLMProxyAPIKeyRequest](schemas.md#schemacreatellmproxyapikeyrequest)|true|API key creation details| > Example responses - +> > 201 Response ```json @@ -1043,7 +1043,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:api_key:cr "status": "success", "message": "API key created and broadcasted to gateways successfully", "id": "production-key", - "apiKey": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456" + "apiKey": "REDACTED_API_KEY" } ``` @@ -1056,8 +1056,8 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:api_key:cr "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1158,7 +1158,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:api_key:re |offset|query|integer|false|Zero-based index of the first item to return.| > Example responses - +> > 200 Response ```json @@ -1261,7 +1261,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:api_key:de |apiKeyId|path|string|true|Name of the API key to delete| > Example responses - +> > 401 Response ```json diff --git a/docs/rest-apis/platform-api/llm-proxy-deployments.md b/docs/rest-apis/platform-api/llm-proxy-deployments.md index fed947c912..081ab16263 100644 --- a/docs/rest-apis/platform-api/llm-proxy-deployments.md +++ b/docs/rest-apis/platform-api/llm-proxy-deployments.md @@ -53,19 +53,18 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment |body|body|[DeployRequest](schemas.md#schemadeployrequest)|true|Deployment request with gateway ID, base reference, and metadata| > Example responses - -> 201 Response +> +> Asynchronous operation accepted; poll the deployment until status becomes DEPLOYED or FAILED. ```json { "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", "name": "v1.0-production", "gatewayId": "prod-gateway-01", - "status": "DEPLOYED", + "status": "DEPLOYING", "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", "metadata": {}, "createdAt": "2019-08-24T14:15:22Z", - "statusReason": "string", "updatedAt": "2019-08-24T14:15:22Z" } ``` @@ -79,8 +78,8 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -161,7 +160,7 @@ curl -X GET https://localhost:9243/api/v0.9/llm-proxies/{llmProxyId}/deployments ``` Retrieves all deployment artifacts for a specific LLM proxy. The proxyId parameter is the -LLM proxy handle (identifier), not the UUID. Supports filtering by gateway UUID and deployment status. +LLM proxy handle (identifier), not the UUID. Supports filtering by gateway handle and deployment status. Access is validated against the organization in the JWT token. ### Authentication @@ -178,14 +177,14 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment |Name|In|Type|Required|Description| |---|---|---|---|---| |llmProxyId|path|string|true|Unique identifier of the LLM proxy| -|gatewayId|query|string|false|**Gateway ID** consisting of the **UUID** of the Gateway to filter status by.| +|gatewayId|query|string|false|**Gateway ID** (handle — unique slug identifier) of the Gateway to filter deployments by.| |status|query|string|false|Filter deployments by status (DEPLOYED, UNDEPLOYED, DEPLOYING, UNDEPLOYING, FAILED, or ARCHIVED)| |limit|query|integer|false|Maximum number of items to return per page.| |offset|query|integer|false|Zero-based index of the first item to return.| #### Detailed descriptions -**gatewayId**: **Gateway ID** consisting of the **UUID** of the Gateway to filter status by. +**gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway to filter deployments by. #### Enumerated Values @@ -199,7 +198,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment |status|ARCHIVED| > Example responses - +> > 200 Response ```json @@ -235,8 +234,8 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -319,7 +318,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment |deploymentId|path|string(uuid)|true|The UUID of the deployment| > Example responses - +> > 200 Response ```json @@ -412,7 +411,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment |deploymentId|path|string(uuid)|true|The UUID of the deployment| > Example responses - +> > 400 Response ```json @@ -422,8 +421,8 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -464,8 +463,8 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment ```json { "status": "error", - "code": "CONFLICT", - "message": "The specified resource already exists." + "code": "DEPLOYMENT_ACTIVE", + "message": "Cannot delete an active deployment - undeploy it first." } ``` @@ -489,7 +488,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The deployment is still active and must be undeployed before deletion.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ## Undeploy LLM proxy deployment from gateway @@ -533,19 +532,18 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment |gatewayId|query|string|true|UUID of the gateway (validated against deployment's bound gateway)| > Example responses - -> 200 Response +> +> Asynchronous operation accepted; poll the deployment until status becomes UNDEPLOYED or FAILED. ```json { "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", "name": "v1.0-production", "gatewayId": "prod-gateway-01", - "status": "DEPLOYED", + "status": "UNDEPLOYING", "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", "metadata": {}, "createdAt": "2019-08-24T14:15:22Z", - "statusReason": "string", "updatedAt": "2019-08-24T14:15:22Z" } ``` @@ -559,8 +557,8 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -602,7 +600,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -626,7 +624,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ## Restore a previous LLM proxy deployment @@ -670,19 +668,18 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment |gatewayId|query|string|true|UUID of the gateway (validated against deployment's bound gateway)| > Example responses - -> 200 Response +> +> Asynchronous operation accepted; poll the deployment until status becomes DEPLOYED or FAILED. ```json { "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", "name": "v1.0-production", "gatewayId": "prod-gateway-01", - "status": "DEPLOYED", + "status": "DEPLOYING", "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", "metadata": {}, "createdAt": "2019-08-24T14:15:22Z", - "statusReason": "string", "updatedAt": "2019-08-24T14:15:22Z" } ``` @@ -696,8 +693,8 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -739,7 +736,7 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -763,5 +760,5 @@ Required scopes (the token must carry at least one of): `ap:llm_proxy:deployment |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/mcp-proxies.md b/docs/rest-apis/platform-api/mcp-proxies.md index 446e20db60..6d3810ce99 100644 --- a/docs/rest-apis/platform-api/mcp-proxies.md +++ b/docs/rest-apis/platform-api/mcp-proxies.md @@ -36,7 +36,6 @@ Deploy a new MCP proxy configuration. "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -45,7 +44,6 @@ Deploy a new MCP proxy configuration. }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -104,7 +102,7 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:create`, ` |body|body|[MCPProxy](schemas.md#schemamcpproxy)|true|none| > Example responses - +> > 201 Response ```json @@ -122,18 +120,18 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:create`, ` "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } } }, @@ -183,8 +181,8 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:create`, ` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -216,7 +214,7 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:create`, ` { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -239,7 +237,7 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:create`, ` |400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ### Response Headers @@ -279,16 +277,16 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:read`, `ap |Name|In|Type|Required|Description| |---|---|---|---|---| -|projectId|query|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by.| +|projectId|query|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project whose resources should be returned.| |limit|query|integer|false|Maximum number of items to return per page.| |offset|query|integer|false|Zero-based index of the first item to return.| #### Detailed descriptions -**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by. +**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project whose resources should be returned. > Example responses - +> > 200 Response ```json @@ -381,7 +379,7 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:read`, `ap |mcpProxyId|path|string|true|Unique identifier of the MCP proxy| > Example responses - +> > 200 Response ```json @@ -399,18 +397,18 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:read`, `ap "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } } }, @@ -525,7 +523,6 @@ Update the configuration of an existing MCP proxy. "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -534,7 +531,6 @@ Update the configuration of an existing MCP proxy. }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -594,7 +590,7 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:update`, ` |body|body|[MCPProxy](schemas.md#schemamcpproxy)|true|none| > Example responses - +> > 200 Response ```json @@ -612,18 +608,18 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:update`, ` "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } } }, @@ -673,8 +669,8 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:update`, ` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -766,7 +762,7 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:delete`, ` |mcpProxyId|path|string|true|Unique identifier of the MCP proxy| > Example responses - +> > 400 Response ```json @@ -776,8 +772,8 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:delete`, ` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -883,10 +879,10 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:read`, `ap |Name|In|Type|Required|Description| |---|---|---|---|---| -|body|body|[MCPServerInfoFetchRequest](schemas.md#schemamcpserverinfofetchrequest)|true|Deployment request with gateway ID, base reference, and metadata| +|body|body|[MCPServerInfoFetchRequest](schemas.md#schemamcpserverinfofetchrequest)|true|Target MCP server to introspect — either a direct `url` (with optional `auth`), or a `proxyId` to refetch using a stored proxy configuration.| > Example responses - +> > 200 Response ```json @@ -913,8 +909,8 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:read`, `ap "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } diff --git a/docs/rest-apis/platform-api/mcp-proxy-deployments.md b/docs/rest-apis/platform-api/mcp-proxy-deployments.md index 5c6dab0934..72f2d7f802 100644 --- a/docs/rest-apis/platform-api/mcp-proxy-deployments.md +++ b/docs/rest-apis/platform-api/mcp-proxy-deployments.md @@ -53,19 +53,18 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment |body|body|[DeployRequest](schemas.md#schemadeployrequest)|true|Deployment request with gateway ID, base reference, and metadata| > Example responses - -> 201 Response +> +> Asynchronous operation accepted; poll the deployment until status becomes DEPLOYED or FAILED. ```json { "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", "name": "v1.0-production", "gatewayId": "prod-gateway-01", - "status": "DEPLOYED", + "status": "DEPLOYING", "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", "metadata": {}, "createdAt": "2019-08-24T14:15:22Z", - "statusReason": "string", "updatedAt": "2019-08-24T14:15:22Z" } ``` @@ -79,8 +78,8 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -150,7 +149,7 @@ curl -X GET https://localhost:9243/api/v0.9/mcp-proxies/{mcpProxyId}/deployments ``` Retrieves all deployment artifacts for a specific MCP proxy. The id parameter is the MCP proxy handle (identifier), -not the UUID. Supports filtering by gateway UUID and deployment status. +not the UUID. Supports filtering by gateway handle and deployment status. Access is validated against the organization in the JWT token. ### Authentication @@ -167,14 +166,14 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment |Name|In|Type|Required|Description| |---|---|---|---|---| |mcpProxyId|path|string|true|Unique identifier of the MCP proxy| -|gatewayId|query|string|false|**Gateway ID** consisting of the **UUID** of the Gateway to filter status by.| +|gatewayId|query|string|false|**Gateway ID** (handle — unique slug identifier) of the Gateway to filter deployments by.| |status|query|string|false|Filter deployments by status (DEPLOYED, UNDEPLOYED, DEPLOYING, UNDEPLOYING, FAILED, or ARCHIVED)| |limit|query|integer|false|Maximum number of items to return per page.| |offset|query|integer|false|Zero-based index of the first item to return.| #### Detailed descriptions -**gatewayId**: **Gateway ID** consisting of the **UUID** of the Gateway to filter status by. +**gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway to filter deployments by. #### Enumerated Values @@ -188,7 +187,7 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment |status|ARCHIVED| > Example responses - +> > 200 Response ```json @@ -224,8 +223,8 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -308,7 +307,7 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment |deploymentId|path|string(uuid)|true|The UUID of the deployment| > Example responses - +> > 200 Response ```json @@ -401,7 +400,7 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment |deploymentId|path|string(uuid)|true|The UUID of the deployment| > Example responses - +> > 400 Response ```json @@ -411,8 +410,8 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -443,8 +442,8 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment ```json { "status": "error", - "code": "CONFLICT", - "message": "The specified resource already exists." + "code": "DEPLOYMENT_ACTIVE", + "message": "Cannot delete an active deployment - undeploy it first." } ``` @@ -467,7 +466,7 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment |400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The deployment is still active and must be undeployed before deletion.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ## Undeploy deployment from gateway @@ -511,19 +510,18 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment |gatewayId|query|string|true|Handle (URL-friendly slug) of the gateway (validated against deployment's bound gateway)| > Example responses - -> 200 Response +> +> Asynchronous operation accepted; poll the deployment until status becomes UNDEPLOYED or FAILED. ```json { "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", "name": "v1.0-production", "gatewayId": "prod-gateway-01", - "status": "DEPLOYED", + "status": "UNDEPLOYING", "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", "metadata": {}, "createdAt": "2019-08-24T14:15:22Z", - "statusReason": "string", "updatedAt": "2019-08-24T14:15:22Z" } ``` @@ -537,8 +535,8 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -570,7 +568,7 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -593,7 +591,7 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment |400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ## Restore a previous deployment @@ -637,19 +635,18 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment |gatewayId|query|string|true|Handle (URL-friendly slug) of the gateway (validated against deployment's bound gateway)| > Example responses - -> 200 Response +> +> Asynchronous operation accepted; poll the deployment until status becomes DEPLOYED or FAILED. ```json { "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", "name": "v1.0-production", "gatewayId": "prod-gateway-01", - "status": "DEPLOYED", + "status": "DEPLOYING", "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", "metadata": {}, "createdAt": "2019-08-24T14:15:22Z", - "statusReason": "string", "updatedAt": "2019-08-24T14:15:22Z" } ``` @@ -663,8 +660,8 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -696,7 +693,7 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -719,5 +716,5 @@ Required scopes (the token must carry at least one of): `ap:mcp_proxy:deployment |400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/organizations.md b/docs/rest-apis/platform-api/organizations.md index bbb79fbd8a..71e172c723 100644 --- a/docs/rest-apis/platform-api/organizations.md +++ b/docs/rest-apis/platform-api/organizations.md @@ -49,7 +49,7 @@ Required scopes (the token must carry at least one of): `ap:organization:create` |body|body|[Organization](schemas.md#schemaorganization)|true|Organization that needs to be added| > Example responses - +> > 201 Response ```json @@ -73,8 +73,8 @@ Required scopes (the token must carry at least one of): `ap:organization:create` "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -86,7 +86,7 @@ Required scopes (the token must carry at least one of): `ap:organization:create` { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -107,7 +107,7 @@ Required scopes (the token must carry at least one of): `ap:organization:create` |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Organization registered successfully|[Organization](schemas.md#schemaorganization)| |400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ### Response Headers @@ -152,7 +152,7 @@ Required scopes (the token must carry at least one of): `ap:organization:read`, |offset|query|integer|false|Zero-based index of the first item to return.| > Example responses - +> > 200 Response ```json @@ -240,7 +240,7 @@ Required scopes (the token must carry at least one of): `ap:organization:read`, |organizationId|path|string|true|ID of the organization.| > Example responses - +> > 200 Response ```json @@ -330,7 +330,7 @@ Required scopes (the token must carry at least one of): `ap:organization:read`, |organizationId|path|string|true|ID of the organization.| > Example responses - +> > 401 Response ```json diff --git a/docs/rest-apis/platform-api/projects.md b/docs/rest-apis/platform-api/projects.md index 228102abe8..f12123f376 100644 --- a/docs/rest-apis/platform-api/projects.md +++ b/docs/rest-apis/platform-api/projects.md @@ -49,7 +49,7 @@ Required scopes (the token must carry at least one of): `ap:project:create`, `ap |body|body|[CreateProjectRequest](schemas.md#schemacreateprojectrequest)|true|none| > Example responses - +> > 201 Response ```json @@ -74,8 +74,8 @@ Required scopes (the token must carry at least one of): `ap:project:create`, `ap "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -107,7 +107,7 @@ Required scopes (the token must carry at least one of): `ap:project:create`, `ap { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -130,7 +130,7 @@ Required scopes (the token must carry at least one of): `ap:project:create`, `ap |400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request. Invalid request or validation error.|[Error](schemas.md#schemaerror)| |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ### Response Headers @@ -187,7 +187,7 @@ Required scopes (the token must carry at least one of): `ap:project:read`, `ap:p |sortOrder|desc| > Example responses - +> > 200 Response ```json @@ -253,7 +253,7 @@ Required scopes (the token must carry at least one of): `ap:project:read`, `ap:p |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| -## Get project by UUID +## Get project by handle @@ -269,8 +269,9 @@ curl -X GET https://localhost:9243/api/v0.9/projects/{projectId} \ ``` -Retrieves a specific project by its UUID. Access is validated against the organization -in the JWT token to ensure users can only access projects in their organization. +Retrieves a specific project by its handle (unique slug identifier). Access is validated +against the organization in the JWT token to ensure users can only access projects in +their organization. ### Authentication @@ -281,7 +282,7 @@ Required scopes (the token must carry at least one of): `ap:project:read`, `ap:p -

Parameters

+

Parameters

|Name|In|Type|Required|Description| |---|---|---|---|---| @@ -292,7 +293,7 @@ Required scopes (the token must carry at least one of): `ap:project:read`, `ap:p **projectId**: **Project ID** (handle — unique slug identifier) of the Project. > Example responses - +> > 200 Response ```json @@ -317,8 +318,8 @@ Required scopes (the token must carry at least one of): `ap:project:read`, `ap:p "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -355,7 +356,7 @@ Required scopes (the token must carry at least one of): `ap:project:read`, `ap:p } ``` -

Responses

+

Responses

|Status|Meaning|Description|Schema| |---|---|---|---| @@ -416,7 +417,7 @@ Required scopes (the token must carry at least one of): `ap:project:update`, `ap **projectId**: **Project ID** (handle — unique slug identifier) of the Project. > Example responses - +> > 200 Response ```json @@ -441,8 +442,8 @@ Required scopes (the token must carry at least one of): `ap:project:update`, `ap "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -484,7 +485,7 @@ Required scopes (the token must carry at least one of): `ap:project:update`, `ap { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -508,7 +509,7 @@ Required scopes (the token must carry at least one of): `ap:project:update`, `ap |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ## Delete project @@ -527,8 +528,8 @@ curl -X DELETE https://localhost:9243/api/v0.9/projects/{projectId} \ ``` -Deletes a specific project by its UUID. Access is validated against the organization -in the JWT token. +Deletes a specific project by its handle (unique slug identifier). Access is validated +against the organization in the JWT token. ### Authentication @@ -550,7 +551,7 @@ Required scopes (the token must carry at least one of): `ap:project:delete`, `ap **projectId**: **Project ID** (handle — unique slug identifier) of the Project. > Example responses - +> > 400 Response ```json @@ -560,8 +561,8 @@ Required scopes (the token must carry at least one of): `ap:project:delete`, `ap "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } diff --git a/docs/rest-apis/platform-api/rest-api-deployments.md b/docs/rest-apis/platform-api/rest-api-deployments.md index cbd7613fe9..8f6fb2bde0 100644 --- a/docs/rest-apis/platform-api/rest-api-deployments.md +++ b/docs/rest-apis/platform-api/rest-api-deployments.md @@ -57,19 +57,18 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. > Example responses - -> 201 Response +> +> Asynchronous operation accepted; poll the deployment until status becomes DEPLOYED or FAILED. ```json { "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", "name": "v1.0-production", "gatewayId": "prod-gateway-01", - "status": "DEPLOYED", + "status": "DEPLOYING", "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", "metadata": {}, "createdAt": "2019-08-24T14:15:22Z", - "statusReason": "string", "updatedAt": "2019-08-24T14:15:22Z" } ``` @@ -83,8 +82,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -165,7 +164,7 @@ curl -X GET https://localhost:9243/api/v0.9/rest-apis/{restApiId}/deployments \ ``` Retrieves all deployment artifacts for a specific API. The apiId parameter is the API handle (identifier), -not the UUID. Supports filtering by gateway UUID and deployment status. +not the UUID. Supports filtering by gateway handle and deployment status. Access is validated against the organization in the JWT token. ### Authentication @@ -182,7 +181,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: |Name|In|Type|Required|Description| |---|---|---|---|---| |restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| -|gatewayId|query|string|false|**Gateway ID** consisting of the **UUID** of the Gateway to filter status by.| +|gatewayId|query|string|false|**Gateway ID** (handle — unique slug identifier) of the Gateway to filter deployments by.| |status|query|string|false|Filter deployments by status (DEPLOYED, UNDEPLOYED, DEPLOYING, UNDEPLOYING, FAILED, or ARCHIVED)| |limit|query|integer|false|Maximum number of items to return per page.| |offset|query|integer|false|Zero-based index of the first item to return.| @@ -191,7 +190,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. -**gatewayId**: **Gateway ID** consisting of the **UUID** of the Gateway to filter status by. +**gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway to filter deployments by. #### Enumerated Values @@ -205,7 +204,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: |status|ARCHIVED| > Example responses - +> > 200 Response ```json @@ -241,8 +240,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -329,7 +328,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. > Example responses - +> > 200 Response ```json @@ -426,7 +425,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. > Example responses - +> > 400 Response ```json @@ -436,8 +435,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -478,8 +477,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: ```json { "status": "error", - "code": "CONFLICT", - "message": "The specified resource already exists." + "code": "DEPLOYMENT_ACTIVE", + "message": "Cannot delete an active deployment - undeploy it first." } ``` @@ -503,7 +502,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The deployment is still active and must be undeployed before deletion.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ## Undeploy deployment from gateway @@ -551,19 +550,18 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. > Example responses - -> 200 Response +> +> Asynchronous operation accepted; poll the deployment until status becomes UNDEPLOYED or FAILED. ```json { "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", "name": "v1.0-production", "gatewayId": "prod-gateway-01", - "status": "DEPLOYED", + "status": "UNDEPLOYING", "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", "metadata": {}, "createdAt": "2019-08-24T14:15:22Z", - "statusReason": "string", "updatedAt": "2019-08-24T14:15:22Z" } ``` @@ -577,8 +575,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -620,7 +618,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -644,7 +642,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ## Restore a previous deployment @@ -692,19 +690,18 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. > Example responses - -> 200 Response +> +> Asynchronous operation accepted; poll the deployment until status becomes DEPLOYED or FAILED. ```json { "deploymentId": "a73c85a1-d857-491e-a6b2-51dce05de7a2", "name": "v1.0-production", "gatewayId": "prod-gateway-01", - "status": "DEPLOYED", + "status": "DEPLOYING", "baseDeploymentId": "be6d8692-b9de-400e-b6c1-14db50154e27", "metadata": {}, "createdAt": "2019-08-24T14:15:22Z", - "statusReason": "string", "updatedAt": "2019-08-24T14:15:22Z" } ``` @@ -718,8 +715,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -761,7 +758,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -785,5 +782,5 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| diff --git a/docs/rest-apis/platform-api/rest-apis.md b/docs/rest-apis/platform-api/rest-apis.md index 07acbecdc3..258ab1cd3d 100644 --- a/docs/rest-apis/platform-api/rest-apis.md +++ b/docs/rest-apis/platform-api/rest-apis.md @@ -36,7 +36,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:read`, `ap: |Name|In|Type|Required|Description| |---|---|---|---|---| -|projectId|query|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by.| +|projectId|query|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project whose resources should be returned.| |limit|query|integer|false|Maximum number of items to return per page.| |offset|query|integer|false|Zero-based index of the first item to return.| |sortBy|query|string|false|Field to sort the collection by. An unrecognized value falls back to the default sort (createdAt).| @@ -45,7 +45,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:read`, `ap: #### Detailed descriptions -**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project to filter APIs by. +**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project whose resources should be returned. #### Enumerated Values @@ -57,7 +57,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:read`, `ap: |sortOrder|desc| > Example responses - +> > 200 Response ```json @@ -68,7 +68,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:read`, `ap: "id": "my-rest-api-handle", "displayName": "PizzaShackAPI", "description": "This is a simple API for Pizza Shack online pizza delivery store", - "context": "pizza", + "context": "/pizza", "version": "1.0.0", "createdBy": "john.doe", "updatedBy": "john.doe", @@ -79,18 +79,18 @@ Required scopes (the token must carry at least one of): `ap:rest_api:read`, `ap: "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } } }, @@ -176,8 +176,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:read`, `ap: "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -252,13 +252,12 @@ belong to the organization specified in the JWT token. "id": "my-rest-api-handle", "displayName": "PizzaShackAPI", "description": "This is a simple API for Pizza Shack online pizza delivery store", - "context": "pizza", + "context": "/pizza", "version": "1.0.0", "projectId": "default-project", "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -267,7 +266,6 @@ belong to the organization specified in the JWT token. }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -357,7 +355,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:create`, `a |body|body|[CreateRESTAPIRequest](schemas.md#schemacreaterestapirequest)|true|API object that needs to be added| > Example responses - +> > 201 Response ```json @@ -365,7 +363,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:create`, `a "id": "my-rest-api-handle", "displayName": "PizzaShackAPI", "description": "This is a simple API for Pizza Shack online pizza delivery store", - "context": "pizza", + "context": "/pizza", "version": "1.0.0", "createdBy": "john.doe", "updatedBy": "john.doe", @@ -376,18 +374,18 @@ Required scopes (the token must carry at least one of): `ap:rest_api:create`, `a "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } } }, @@ -466,8 +464,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:create`, `a "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -509,7 +507,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:create`, `a { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -533,7 +531,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:create`, `a |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ### Response Headers @@ -581,7 +579,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:read`, `ap: **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. > Example responses - +> > 200 Response ```json @@ -589,7 +587,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:read`, `ap: "id": "my-rest-api-handle", "displayName": "PizzaShackAPI", "description": "This is a simple API for Pizza Shack online pizza delivery store", - "context": "pizza", + "context": "/pizza", "version": "1.0.0", "createdBy": "john.doe", "updatedBy": "john.doe", @@ -600,18 +598,18 @@ Required scopes (the token must carry at least one of): `ap:rest_api:read`, `ap: "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } } }, @@ -690,8 +688,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:read`, `ap: "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -766,13 +764,12 @@ in the JWT token. "id": "my-rest-api-handle", "displayName": "PizzaShackAPI", "description": "This is a simple API for Pizza Shack online pizza delivery store", - "context": "pizza", + "context": "/pizza", "version": "1.0.0", "projectId": "default-project", "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -781,7 +778,6 @@ in the JWT token. }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -876,7 +872,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:update`, `a **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. > Example responses - +> > 200 Response ```json @@ -884,7 +880,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:update`, `a "id": "my-rest-api-handle", "displayName": "PizzaShackAPI", "description": "This is a simple API for Pizza Shack online pizza delivery store", - "context": "pizza", + "context": "/pizza", "version": "1.0.0", "createdBy": "john.doe", "updatedBy": "john.doe", @@ -895,18 +891,18 @@ Required scopes (the token must carry at least one of): `ap:rest_api:update`, `a "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", - "header": "X-API-Key" + "header": "X-API-Key", + "value": "my-api-key-value" } } }, @@ -985,8 +981,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:update`, `a "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1028,7 +1024,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:update`, `a { "status": "error", "code": "CONFLICT", - "message": "The specified resource already exists." + "message": "The request conflicts with the current state of the resource." } ``` @@ -1052,7 +1048,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:update`, `a |401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized. Authentication credentials are missing or invalid.|[Error](schemas.md#schemaerror)| |403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Forbidden. The authenticated user does not have permission to access this resource.|[Error](schemas.md#schemaerror)| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found. The specified resource does not exist.|[Error](schemas.md#schemaerror)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. Specified resource already exists.|[Error](schemas.md#schemaerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict. The request conflicts with the current state of the resource.|[Error](schemas.md#schemaerror)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error.|[Error](schemas.md#schemaerror)| ## Delete REST API @@ -1094,7 +1090,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:delete`, `a **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. > Example responses - +> > 400 Response ```json @@ -1104,8 +1100,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:delete`, `a "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1205,7 +1201,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:gateway:rea **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. > Example responses - +> > 200 Response ```json @@ -1258,8 +1254,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:gateway:rea "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1360,7 +1356,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:gateway:cre **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. > Example responses - +> > 200 Response ```json @@ -1413,8 +1409,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:gateway:cre "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1532,7 +1528,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:api_key:cre **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. > Example responses - +> > 201 Response ```json @@ -1552,8 +1548,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:api_key:cre "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1690,7 +1686,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:api_key:upd **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. > Example responses - +> > 200 Response ```json @@ -1710,8 +1706,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:api_key:upd "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } @@ -1822,7 +1818,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:api_key:del **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. > Example responses - +> > 400 Response ```json @@ -1832,8 +1828,8 @@ Required scopes (the token must carry at least one of): `ap:rest_api:api_key:del "message": "The request failed validation.", "errors": [ { - "field": "spec.context", - "message": "must start with /" + "field": "", + "message": "" } ] } diff --git a/docs/rest-apis/platform-api/schemas.md b/docs/rest-apis/platform-api/schemas.md index 63ab2d2804..8df491ef47 100644 --- a/docs/rest-apis/platform-api/schemas.md +++ b/docs/rest-apis/platform-api/schemas.md @@ -747,7 +747,7 @@ Request body for creating an application. "id": "my-rest-api-handle", "displayName": "PizzaShackAPI", "description": "This is a simple API for Pizza Shack online pizza delivery store", - "context": "pizza", + "context": "/pizza", "version": "1.0.0", "createdBy": "john.doe", "updatedBy": "john.doe", @@ -758,7 +758,6 @@ Request body for creating an application. "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -767,7 +766,6 @@ Request body for creating an application. }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -1747,7 +1745,7 @@ List of gateway status information for polling "id": "my-rest-api-handle", "displayName": "PizzaShackAPI", "description": "This is a simple API for Pizza Shack online pizza delivery store", - "context": "pizza", + "context": "/pizza", "version": "1.0.0", "createdBy": "john.doe", "updatedBy": "john.doe", @@ -1758,7 +1756,6 @@ List of gateway status information for polling "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -1767,7 +1764,6 @@ List of gateway status information for polling }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -1867,7 +1863,7 @@ List of gateway status information for polling ```json { "id": "def45678-g901-23hi-j456-789012klmnop", - "token": "nM8pQ1rT4vW7yZ0bC3eF6hI9kL2nO5qR8tU1wX4zA7cE0fH3jK6mP9sV2yB5dG8i", + "token": "REDACTED_TOKEN", "createdAt": "2025-10-15T14:20:00Z", "message": "New token generated successfully. Old token remains active until revoked." } @@ -1879,7 +1875,7 @@ List of gateway status information for polling |Name|Type|Required|Restrictions|Description| |---|---|---|---|---| |id|string(uuid)|false|none|ID of the newly generated token| -|token|string|false|none|Plain-text new authentication token (only exposed once during rotation)| +|token|string|false|none|Plain-text new authentication token (only exposed once during rotation). The example value is a non-functional placeholder.| |createdAt|string(date-time)|false|none|Timestamp when new token was created| |message|string|false|none|Informational message about token rotation| @@ -1928,7 +1924,7 @@ List of gateway status information for polling "id": "my-rest-api-handle", "displayName": "PizzaShackAPI", "description": "This is a simple API for Pizza Shack online pizza delivery store", - "context": "pizza", + "context": "/pizza", "version": "1.0.0", "createdBy": "john.doe", "updatedBy": "john.doe", @@ -1939,7 +1935,6 @@ List of gateway status information for polling "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -1948,7 +1943,6 @@ List of gateway status information for polling }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -2571,7 +2565,7 @@ Time unit for API key expiration duration "message": "The requested REST API could not be found.", "errors": [ { - "field": "spec.context", + "field": "", "message": "must start with /" } ], @@ -2609,7 +2603,7 @@ Standard error response ```json { - "field": "spec.context", + "field": "", "message": "must start with /" } @@ -2748,7 +2742,6 @@ Field-level validation error { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -2757,7 +2750,6 @@ Field-level validation error }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -2774,8 +2766,8 @@ Upstream backend configuration with main and sandbox endpoints |Name|Type|Required|Restrictions|Description| |---|---|---|---|---| -|main|[UpstreamDefinition](#schemaupstreamdefinition)|true|none|Upstream endpoint configuration (single target or reference)| -|sandbox|[UpstreamDefinition](#schemaupstreamdefinition)|false|none|Upstream endpoint configuration (single target or reference)| +|main|[UpstreamDefinition](#schemaupstreamdefinition)|true|none|Upstream endpoint configuration. Provide exactly one of `url` (a direct backend URL) or
`ref` (a reference to a predefined upstream definition) — never both.| +|sandbox|[UpstreamDefinition](#schemaupstreamdefinition)|false|none|Upstream endpoint configuration. Provide exactly one of `url` (a direct backend URL) or
`ref` (a reference to a predefined upstream definition) — never both.|

UpstreamDefinition

@@ -2787,7 +2779,6 @@ Upstream backend configuration with main and sandbox endpoints ```json { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -2797,14 +2788,15 @@ Upstream backend configuration with main and sandbox endpoints ``` -Upstream endpoint configuration (single target or reference) +Upstream endpoint configuration. Provide exactly one of `url` (a direct backend URL) or +`ref` (a reference to a predefined upstream definition) — never both. ### Properties |Name|Type|Required|Restrictions|Description| |---|---|---|---|---| -|url|string(uri)|false|none|Direct backend URL to route traffic to| -|ref|string|false|none|Reference to a predefined upstreamDefinition| +|url|string(uri)|false|none|Direct backend URL to route traffic to. Mutually exclusive with `ref`.| +|ref|string|false|none|Reference to a predefined upstreamDefinition. Mutually exclusive with `url`.| |auth|[UpstreamAuth](#schemaupstreamauth)|false|none|Authentication configuration for upstream endpoints| oneOf @@ -2956,23 +2948,23 @@ Authentication configuration for upstream endpoints }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" } } @@ -3008,23 +3000,23 @@ Authentication configuration for upstream endpoints }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" } } ] @@ -3075,23 +3067,23 @@ Authentication configuration for upstream endpoints }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "resourceMappings": { "resources": [ @@ -3103,23 +3095,23 @@ Authentication configuration for upstream endpoints }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" } } ] @@ -3187,23 +3179,23 @@ Authentication configuration for upstream endpoints }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "resourceMappings": { "resources": [ @@ -3215,23 +3207,23 @@ Authentication configuration for upstream endpoints }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.outputTokens" }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.totalTokens" }, "remainingTokens": { - "location": "payload", - "identifier": "$.usage.inputTokens" + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.model" } } ] @@ -4054,7 +4046,7 @@ Limit definition with independent request/token/cost dimensions. If all dimensio "updatedBy": "john.doe", "version": "v1.0", "context": "/openai", - "vhost": "api.openai", + "vhost": "api.openai.com", "template": "openai", "openapi": "openapi: 3.0.3\ninfo:\n title: Provider API\n version: v1.0\npaths: {}\n", "modelProviders": [ @@ -4073,7 +4065,6 @@ Limit definition with independent request/token/cost dimensions. If all dimensio "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -4082,7 +4073,6 @@ Limit definition with independent request/token/cost dimensions. If all dimensio }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -4271,7 +4261,7 @@ Limit definition with independent request/token/cost dimensions. If all dimensio |readOnly|boolean|false|read-only|True if the artifact originated from a data-plane gateway (origin gateway_api) and is read-only in the control plane; false for control-plane created artifacts.| |updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /llm-providers/{id}), omitted from list responses.| |version|string|true|none|Semantic version of the LLM Provider| -|context|string|false|none|Base path for all REST API routes (must start with /, no trailing slash)| +|context|string|false|none|Base path for all routes exposed by this proxy. Must start with / and carry no trailing slash; the single exception is the root path "/", which is the default.| |vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| |template|string|true|none|Template name to use for this LLM Provider| |openapi|string|false|none|OpenAPI specification (JSON or YAML) for the provider endpoint| @@ -4470,7 +4460,7 @@ Limit definition with independent request/token/cost dimensions. If all dimensio "version": "v1.0", "projectId": "550e8400-e29b-41d4-a716-446655440000", "context": "/openai", - "vhost": "api.openai", + "vhost": "api.openai.com", "provider": { "id": "wso2-openai-provider", "auth": { @@ -4568,7 +4558,7 @@ Limit definition with independent request/token/cost dimensions. If all dimensio |updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /llm-proxies/{id}), omitted from list responses.| |version|string|true|none|Semantic version of the LLM proxy| |projectId|string|true|none|UUID of the project this proxy belongs to| -|context|string|false|none|Base path for all REST API routes (must start with /, no trailing slash)| +|context|string|false|none|Base path for all routes exposed by this proxy. Must start with / and carry no trailing slash; the single exception is the root path "/", which is the default.| |vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| |provider|[LLMProxyProvider](#schemallmproxyprovider)|true|none|none| |additionalProviders|[[LLMProxyAdditionalProvider](#schemallmproxyadditionalprovider)]|false|none|Optional list of additional LLM providers attached to this proxy as selectable upstreams. Policies route requests to any of these by setting the upstream name. The primary `provider` field above remains the default upstream and the FK target.| @@ -4796,7 +4786,7 @@ Request/response translator applied when this provider is the selected upstream. "status": "success", "message": "API key created and broadcasted to gateways successfully", "id": "production-key", - "apiKey": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456" + "apiKey": "REDACTED_API_KEY" } ``` @@ -4808,7 +4798,7 @@ Request/response translator applied when this provider is the selected upstream. |status|string|true|none|Status of the operation| |message|string|true|none|Detailed message about the operation result| |id|string|true|none|Unique identifier of the generated key| -|apiKey|string|true|none|The generated API key value (shown only once, 64 hexadecimal characters)| +|apiKey|string|true|none|The generated API key value — 64 hexadecimal characters, returned only in this creation response and never retrievable afterwards. The example value is a non-functional placeholder.|

CreateLLMProxyAPIKeyRequest

@@ -4850,7 +4840,7 @@ Request/response translator applied when this provider is the selected upstream. "status": "success", "message": "API key created and broadcasted to gateways successfully", "id": "production-key", - "apiKey": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456" + "apiKey": "REDACTED_API_KEY" } ``` @@ -4862,7 +4852,7 @@ Request/response translator applied when this provider is the selected upstream. |status|string|true|none|Status of the operation| |message|string|true|none|Detailed message about the operation result| |id|string|true|none|Unique identifier of the generated key| -|apiKey|string|true|none|The generated API key value (shown only once, 64 hexadecimal characters)| +|apiKey|string|true|none|The generated API key value — 64 hexadecimal characters, returned only in this creation response and never retrievable afterwards. The example value is a non-functional placeholder.|

MCPProxy

@@ -4886,7 +4876,6 @@ Request/response translator applied when this provider is the selected upstream. "upstream": { "main": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -4895,7 +4884,6 @@ Request/response translator applied when this provider is the selected upstream. }, "sandbox": { "url": "http://prod-backend:5000/api/v2", - "ref": "string", "auth": { "type": "api-key", "header": "X-API-Key", @@ -4953,7 +4941,7 @@ Request/response translator applied when this provider is the selected upstream. |updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /mcp-proxies/{id}), omitted from list responses.| |version|string|true|none|Semantic version of the MCP proxy| |projectId|string|false|none|UUID of the project this proxy belongs to| -|context|string|false|none|Base path for all REST API routes (must start with /, no trailing slash)| +|context|string|false|none|Base path for all routes exposed by this proxy. Must start with / and carry no trailing slash; the single exception is the root path "/", which is the default.| |vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| |upstream|[Upstream](#schemaupstream)|true|none|Upstream backend configuration with main and sandbox endpoints| |mcpSpecVersion|string|false|none|MCP specification version supported by this proxy| diff --git a/docs/rest-apis/platform-api/secrets.md b/docs/rest-apis/platform-api/secrets.md index bdf75357dc..f5c58d4cb1 100644 --- a/docs/rest-apis/platform-api/secrets.md +++ b/docs/rest-apis/platform-api/secrets.md @@ -16,23 +16,16 @@ curl -X POST https://localhost:9243/api/v0.9/secrets \ -H 'Authorization: Bearer {access_token}' \ -H 'Content-Type: multipart/form-data' \ -H 'Accept: application/json' \ - -d @payload.json + -F 'id=wso2-openai-key' \ + -F 'displayName=WSO2 OpenAI API Key' \ + -F 'description=Primary API key for WSO2 OpenAI integration' \ + -F 'value=sk-xxx' \ + -F 'type=GENERIC' ``` Create a new encrypted secret scoped to the organization. The plaintext value is never returned. -> Payload - -```yaml -id: wso2-openai-key -displayName: WSO2 OpenAI API Key -description: Primary API key for WSO2 OpenAI integration -value: sk-xxx -type: GENERIC - -``` - ### Authentication -

Parameters

+

Parameters

|Name|In|Type|Required|Description| |---|---|---|---|---| -|projectId|path|string|true|**Project ID** (handle — unique slug identifier) of the Project.| +|projectId|path|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project.| #### Detailed descriptions -**projectId**: **Project ID** (handle — unique slug identifier) of the Project. +**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project. > Example responses > @@ -356,7 +355,7 @@ Required scopes (the token must carry at least one of): `ap:project:read`, `ap:p } ``` -

Responses

+

Responses

|Status|Meaning|Description|Schema| |---|---|---|---| @@ -409,12 +408,12 @@ Required scopes (the token must carry at least one of): `ap:project:update`, `ap |Name|In|Type|Required|Description| |---|---|---|---|---| -|projectId|path|string|true|**Project ID** (handle — unique slug identifier) of the Project.| +|projectId|path|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project.| |body|body|[Project](schemas.md#schemaproject)|true|none| #### Detailed descriptions -**projectId**: **Project ID** (handle — unique slug identifier) of the Project. +**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project. > Example responses > @@ -544,11 +543,11 @@ Required scopes (the token must carry at least one of): `ap:project:delete`, `ap |Name|In|Type|Required|Description| |---|---|---|---|---| -|projectId|path|string|true|**Project ID** (handle — unique slug identifier) of the Project.| +|projectId|path|string|true|**Project ID** consisting of the **handle** (unique slug identifier) of the Project.| #### Detailed descriptions -**projectId**: **Project ID** (handle — unique slug identifier) of the Project. +**projectId**: **Project ID** consisting of the **handle** (unique slug identifier) of the Project. > Example responses > diff --git a/docs/rest-apis/platform-api/rest-api-deployments.md b/docs/rest-apis/platform-api/rest-api-deployments.md index 8f6fb2bde0..685aa8a548 100644 --- a/docs/rest-apis/platform-api/rest-api-deployments.md +++ b/docs/rest-apis/platform-api/rest-api-deployments.md @@ -181,7 +181,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: |Name|In|Type|Required|Description| |---|---|---|---|---| |restApiId|path|string|true|**API ID** consisting of the **handle** (unique identifier) of the API.| -|gatewayId|query|string|false|**Gateway ID** (handle — unique slug identifier) of the Gateway to filter deployments by.| +|gatewayId|query|string|false|**Gateway ID** consisting of the **handle** (unique slug identifier) of the Gateway to filter status by.| |status|query|string|false|Filter deployments by status (DEPLOYED, UNDEPLOYED, DEPLOYING, UNDEPLOYING, FAILED, or ARCHIVED)| |limit|query|integer|false|Maximum number of items to return per page.| |offset|query|integer|false|Zero-based index of the first item to return.| @@ -190,7 +190,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:deployment: **restApiId**: **API ID** consisting of the **handle** (unique identifier) of the API. -**gatewayId**: **Gateway ID** (handle — unique slug identifier) of the Gateway to filter deployments by. +**gatewayId**: **Gateway ID** consisting of the **handle** (unique slug identifier) of the Gateway to filter status by. #### Enumerated Values diff --git a/docs/rest-apis/platform-api/rest-apis.md b/docs/rest-apis/platform-api/rest-apis.md index 258ab1cd3d..9a69c34382 100644 --- a/docs/rest-apis/platform-api/rest-apis.md +++ b/docs/rest-apis/platform-api/rest-apis.md @@ -540,7 +540,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:create`, `a |---|---|---|---|---| |201|Location|string|uri|URL of the newly created resource.| -## Get REST API by UUID +## Get REST API by ID @@ -556,7 +556,7 @@ curl -X GET https://localhost:9243/api/v0.9/rest-apis/{restApiId} \ ``` -Retrieves a specific API by its UUID. Access is validated against the organization +Retrieves a specific API by its ID (handle). Access is validated against the organization in the JWT token. ### Authentication @@ -568,7 +568,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:read`, `ap: -

Parameters

+

Parameters

|Name|In|Type|Required|Description| |---|---|---|---|---| @@ -726,7 +726,7 @@ Required scopes (the token must carry at least one of): `ap:rest_api:read`, `ap: } ``` -

Responses

+

Responses

|Status|Meaning|Description|Schema| |---|---|---|---| @@ -1067,7 +1067,7 @@ curl -X DELETE https://localhost:9243/api/v0.9/rest-apis/{restApiId} \ ``` -Deletes a specific API by its UUID. Access is validated against the organization +Deletes a specific API by its ID (handle). Access is validated against the organization in the JWT token. ### Authentication diff --git a/docs/rest-apis/platform-api/schemas.md b/docs/rest-apis/platform-api/schemas.md index 1ee7412f93..e6b8c72b32 100644 --- a/docs/rest-apis/platform-api/schemas.md +++ b/docs/rest-apis/platform-api/schemas.md @@ -4458,7 +4458,7 @@ Limit definition with independent request/token/cost dimensions. If all dimensio "readOnly": false, "updatedBy": "john.doe", "version": "v1.0", - "projectId": "550e8400-e29b-41d4-a716-446655440000", + "projectId": "default-project", "context": "/openai", "vhost": "api.openai.com", "provider": { @@ -4557,7 +4557,7 @@ Limit definition with independent request/token/cost dimensions. If all dimensio |readOnly|boolean|false|read-only|True if the artifact originated from a data-plane gateway (origin gateway_api) and is read-only in the control plane; false for control-plane created artifacts.| |updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /llm-proxies/{id}), omitted from list responses.| |version|string|true|none|Semantic version of the LLM proxy| -|projectId|string|true|none|UUID of the project this proxy belongs to| +|projectId|string|true|none|Handle (URL-friendly slug) of the project this proxy belongs to| |context|string|false|none|Base path for all routes exposed by this proxy. Must start with / and carry no trailing slash; the single exception is the root path "/", which is the default.| |vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| |provider|[LLMProxyProvider](#schemallmproxyprovider)|true|none|none| @@ -4586,7 +4586,7 @@ Limit definition with independent request/token/cost dimensions. If all dimensio "createdBy": "john.doe", "context": "/wso2-con-assistant", "version": "v1.0", - "projectId": "550e8400-e29b-41d4-a716-446655440000", + "projectId": "default-project", "provider": "wso2-openai-provider", "status": "deployed", "createdAt": "2025-11-25T10:30:00Z", @@ -4606,7 +4606,7 @@ Limit definition with independent request/token/cost dimensions. If all dimensio |createdBy|string|false|read-only|User identifier of the user who created this resource| |context|string|false|none|Context path where the proxy is exposed| |version|string|false|none|none| -|projectId|string|false|none|UUID of the project this proxy belongs to| +|projectId|string|false|none|Handle (URL-friendly slug) of the project this proxy belongs to| |provider|string|false|none|Unique id of a deployed llm provider| |status|string|false|none|none| |createdAt|string(date-time)|false|none|none| @@ -4639,7 +4639,7 @@ Limit definition with independent request/token/cost dimensions. If all dimensio "createdBy": "john.doe", "context": "/wso2-con-assistant", "version": "v1.0", - "projectId": "550e8400-e29b-41d4-a716-446655440000", + "projectId": "default-project", "provider": "wso2-openai-provider", "status": "deployed", "createdAt": "2025-11-25T10:30:00Z", @@ -4870,7 +4870,7 @@ Request/response translator applied when this provider is the selected upstream. "readOnly": false, "updatedBy": "john.doe", "version": "v1.0", - "projectId": "550e8400-e29b-41d4-a716-446655440000", + "projectId": "default-project", "context": "/", "vhost": "mcp.gw.com", "upstream": { @@ -4940,7 +4940,7 @@ Request/response translator applied when this provider is the selected upstream. |readOnly|boolean|false|read-only|True if the artifact originated from a data-plane gateway (origin gateway_api) and is read-only in the control plane; false for control-plane created artifacts.| |updatedBy|string|false|read-only|User identifier of the user who last updated this resource. Only present in the detail response (GET /mcp-proxies/{id}), omitted from list responses.| |version|string|true|none|Semantic version of the MCP proxy| -|projectId|string|false|none|UUID of the project this proxy belongs to| +|projectId|string|false|none|Handle (URL-friendly slug) of the project this proxy belongs to| |context|string|false|none|Base path for all routes exposed by this proxy. Must start with / and carry no trailing slash; the single exception is the root path "/", which is the default.| |vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| |upstream|[Upstream](#schemaupstream)|true|none|Upstream backend configuration with main and sandbox endpoints| @@ -4974,7 +4974,7 @@ Request/response translator applied when this provider is the selected upstream. "createdBy": "john.doe", "context": "/weather-mcp-proxy", "version": "v1.0", - "projectId": "550e8400-e29b-41d4-a716-446655440000", + "projectId": "default-project", "status": "deployed", "mcpSpecVersion": "2025-11-25", "createdAt": "2025-11-25T10:30:00Z", @@ -4994,7 +4994,7 @@ Request/response translator applied when this provider is the selected upstream. |createdBy|string|false|read-only|User identifier of the user who created this resource| |context|string|false|none|Context path where the proxy is exposed| |version|string|false|none|none| -|projectId|string|false|none|UUID of the project this proxy belongs to| +|projectId|string|false|none|Handle (URL-friendly slug) of the project this proxy belongs to| |status|string|false|none|none| |mcpSpecVersion|string|false|none|none| |createdAt|string(date-time)|false|none|none| @@ -5027,7 +5027,7 @@ Request/response translator applied when this provider is the selected upstream. "createdBy": "john.doe", "context": "/weather-mcp-proxy", "version": "v1.0", - "projectId": "550e8400-e29b-41d4-a716-446655440000", + "projectId": "default-project", "status": "deployed", "mcpSpecVersion": "2025-11-25", "createdAt": "2025-11-25T10:30:00Z", From 6acd2cc24d442544611e42a3621e7a7726f5965d Mon Sep 17 00:00:00 2001 From: Thushani Jayasekera Date: Thu, 30 Jul 2026 11:41:43 +0530 Subject: [PATCH 6/7] Enhance upstream validation and redirect handling in HTTP client --- platform-api/internal/service/llm.go | 10 ++++- platform-api/internal/service/llm_test.go | 22 +++++++++++ .../internal/utils/guarded_http_client.go | 38 ++++++++++++++----- .../utils/guarded_http_client_test.go | 38 +++++++++++++++++++ .../internal/utils/openapi_spec_fetcher.go | 13 ++----- 5 files changed, 101 insertions(+), 20 deletions(-) diff --git a/platform-api/internal/service/llm.go b/platform-api/internal/service/llm.go index 5dd1252285..eece1699a3 100644 --- a/platform-api/internal/service/llm.go +++ b/platform-api/internal/service/llm.go @@ -1954,7 +1954,15 @@ func isSQLiteUniqueConstraint(err error) bool { } func validateUpstream(u api.Upstream) error { - return validateUpstreamDefinition("main", u.Main) + if err := validateUpstreamDefinition("main", u.Main); err != nil { + return err + } + // Sandbox is optional, but when present it carries the same either-url-or-ref + // constraint as main. + if u.Sandbox != nil { + return validateUpstreamDefinition("sandbox", *u.Sandbox) + } + return nil } // validateUpstreamDefinition enforces the UpstreamDefinition schema constraint that diff --git a/platform-api/internal/service/llm_test.go b/platform-api/internal/service/llm_test.go index d9e3d4bbe3..e28bc25b0a 100644 --- a/platform-api/internal/service/llm_test.go +++ b/platform-api/internal/service/llm_test.go @@ -2013,3 +2013,25 @@ func TestLLMProxyServiceResolveProjectHandleIsOrgScoped(t *testing.T) { t.Fatalf("expected ProjectNotFound for a cross-org project uuid, got: %v", err) } } + +// The sandbox upstream is optional, but when supplied it carries the same +// exactly-one-of-url-or-ref constraint as main — an unvalidated sandbox would let +// an ambiguous (both) or empty (neither) endpoint through. +func TestValidateUpstreamValidatesSandbox(t *testing.T) { + url := "https://api.example.com" + ref := "openai-default" + main := api.UpstreamDefinition{Url: &url} + + if err := validateUpstream(api.Upstream{Main: main}); err != nil { + t.Fatalf("expected an absent sandbox to be accepted: %v", err) + } + if err := validateUpstream(api.Upstream{Main: main, Sandbox: &api.UpstreamDefinition{Ref: &ref}}); err != nil { + t.Fatalf("expected a ref-only sandbox to be accepted: %v", err) + } + if err := validateUpstream(api.Upstream{Main: main, Sandbox: &api.UpstreamDefinition{Url: &url, Ref: &ref}}); !apperror.ValidationFailed.Is(err) { + t.Fatalf("expected ValidationFailed for a sandbox with both url and ref, got: %v", err) + } + if err := validateUpstream(api.Upstream{Main: main, Sandbox: &api.UpstreamDefinition{}}); !apperror.ValidationFailed.Is(err) { + t.Fatalf("expected ValidationFailed for a sandbox with neither url nor ref, got: %v", err) + } +} diff --git a/platform-api/internal/utils/guarded_http_client.go b/platform-api/internal/utils/guarded_http_client.go index 20a1892f7e..74d378c3e4 100644 --- a/platform-api/internal/utils/guarded_http_client.go +++ b/platform-api/internal/utils/guarded_http_client.go @@ -22,6 +22,7 @@ import ( "fmt" "net" "net/http" + "strings" "time" ) @@ -29,6 +30,33 @@ import ( // through the guarded dialer, so this only bounds redirect loops. const maxOutboundRedirects = 5 +// checkRedirectPolicy builds the CheckRedirect callback shared by every guarded client. It +// bounds redirect loops, keeps the scheme within http/https, and refuses any hop that leaves +// the host of the original request. +// +// The host check is what stops a credential leak: callers pass their own headers on these +// requests (an MCP endpoint's auth header, for instance), and net/http only strips +// Authorization/Cookie-style headers across hosts — a custom header name is forwarded +// verbatim. A malicious or compromised upstream could otherwise answer with a redirect to a +// host it controls and be handed the caller's credential. Same-host redirects are still +// dialed through the guarded dialer, so the address policy continues to apply per hop. +func checkRedirectPolicy(maxRedirects int) func(*http.Request, []*http.Request) error { + return func(req *http.Request, via []*http.Request) error { + if len(via) >= maxRedirects { + return fmt.Errorf("too many redirects") + } + if req.URL.Scheme != "http" && req.URL.Scheme != "https" { + return fmt.Errorf("redirect to a disallowed scheme") + } + // via[0] is the original request; Host carries the port, so a port change counts + // as a different host too. + if len(via) > 0 && !strings.EqualFold(req.URL.Host, via[0].URL.Host) { + return fmt.Errorf("redirect to a different host") + } + return nil + } +} + // guardedDialContext builds a DialContext that resolves the target host itself, refuses to // connect when any resolved address fails the supplied policy, and then dials the exact IP // it just approved. Doing the resolution and the connection in one step is what closes the @@ -134,15 +162,7 @@ func NewUpstreamFetchClient(timeout time.Duration) *http.Client { // that could bypass the address checks. Proxy: nil, }, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - if len(via) >= maxOutboundRedirects { - return fmt.Errorf("too many redirects") - } - if req.URL.Scheme != "http" && req.URL.Scheme != "https" { - return fmt.Errorf("redirect to a disallowed scheme") - } - return nil - }, + CheckRedirect: checkRedirectPolicy(maxOutboundRedirects), } } diff --git a/platform-api/internal/utils/guarded_http_client_test.go b/platform-api/internal/utils/guarded_http_client_test.go index 99ef54d8fe..ffd2074376 100644 --- a/platform-api/internal/utils/guarded_http_client_test.go +++ b/platform-api/internal/utils/guarded_http_client_test.go @@ -99,3 +99,41 @@ func TestUpstreamFetchClientRefusesDeniedAddress(t *testing.T) { t.Fatal("expected the guarded client to refuse a disallowed address") } } + +// TestUpstreamFetchClientRefusesCrossHostRedirect guards the credential-leak case: callers +// pass a custom auth header on MCP calls, and net/http forwards a custom header name across +// hosts, so a redirect off the original host must not be followed. A same-host redirect +// still is. +func TestUpstreamFetchClientRefusesCrossHostRedirect(t *testing.T) { + target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + defer target.Close() + + crossHost := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, target.URL+"/stolen", http.StatusFound) + })) + defer crossHost.Close() + + if _, err := NewUpstreamFetchClient(0).Get(crossHost.URL); err == nil { + t.Fatal("expected the guarded client to refuse a cross-host redirect") + } + + sameHost := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/moved" { + http.Redirect(w, r, "/final", http.StatusFound) + return + } + w.WriteHeader(http.StatusOK) + })) + defer sameHost.Close() + + resp, err := NewUpstreamFetchClient(0).Get(sameHost.URL + "/moved") + if err != nil { + t.Fatalf("expected a same-host redirect to be followed: %v", err) + } + defer resp.Body.Close() //nolint:errcheck + if resp.StatusCode != http.StatusOK { + t.Errorf("expected 200 after a same-host redirect, got %d", resp.StatusCode) + } +} diff --git a/platform-api/internal/utils/openapi_spec_fetcher.go b/platform-api/internal/utils/openapi_spec_fetcher.go index 01a42efc69..82d05bc61b 100644 --- a/platform-api/internal/utils/openapi_spec_fetcher.go +++ b/platform-api/internal/utils/openapi_spec_fetcher.go @@ -51,7 +51,8 @@ const ( // DNS-rebinding) — loopback, private (RFC 1918 / ULA), link-local (incl. the cloud // metadata endpoint 169.254.169.254), unspecified, multicast and broadcast addresses // are refused. -// - Redirects are bounded and each hop is dialed through the same guarded dialer. +// - Redirects are bounded, may not leave the original host, and each hop is dialed +// through the same guarded dialer. // - The response body is read through an io.LimitReader capped at maxBytes. // - Errors are returned sterile (no internal host/IP detail) so callers can log them // internally without leaking infrastructure information to clients. @@ -87,15 +88,7 @@ func FetchOpenAPISpecFromURL(ctx context.Context, rawURL string, maxBytes int64) // that could bypass the IP checks. Proxy: nil, }, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - if len(via) >= openAPISpecMaxRedirects { - return fmt.Errorf("too many redirects") - } - if req.URL.Scheme != "http" && req.URL.Scheme != "https" { - return fmt.Errorf("redirect to a disallowed scheme") - } - return nil - }, + CheckRedirect: checkRedirectPolicy(openAPISpecMaxRedirects), } req, err := http.NewRequestWithContext(ctx, http.MethodGet, parsed.String(), nil) From 29f915797ebd8e51464e725c2fe0a31853767418 Mon Sep 17 00:00:00 2001 From: Thushani Jayasekera Date: Thu, 30 Jul 2026 12:28:46 +0530 Subject: [PATCH 7/7] Implement snackbar notifications for gateway navigation errors in ServiceProviderOverviewTab and ProviderMapTab components --- .../serviceProvider/ProviderMap/ProviderMapTab.tsx | 6 +++++- .../serviceProvider/ServiceProviderOverviewTab.tsx | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ProviderMap/ProviderMapTab.tsx b/portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ProviderMap/ProviderMapTab.tsx index eac26b1ebb..3de7720853 100644 --- a/portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ProviderMap/ProviderMapTab.tsx +++ b/portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ProviderMap/ProviderMapTab.tsx @@ -729,12 +729,16 @@ export default function ProviderMapTab() { logger.error( `Unable to navigate to gateway ${gatewayId} because the current organization is unavailable.` ); + showSnackbar( + 'Unable to open this gateway right now. Please refresh and try again.', + 'error' + ); return; } navigate(buildGatewayPath(currentOrganization, gatewayId)); }, - [currentOrganization, navigate] + [currentOrganization, navigate, showSnackbar] ); const deploymentsByGateway = deployments.reduce< diff --git a/portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderOverviewTab.tsx b/portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderOverviewTab.tsx index 3df3f184ec..dc4bd8e4e9 100644 --- a/portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderOverviewTab.tsx +++ b/portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderOverviewTab.tsx @@ -653,12 +653,16 @@ export default function ServiceProviderOverviewTab({ logger.error( `Unable to navigate to gateway ${gatewayId} because the current organization is unavailable.` ); + showSnackbar( + 'Unable to open this gateway right now. Please refresh and try again.', + 'error' + ); return; } navigate(buildGatewayPath(currentOrganization, gatewayId)); }, - [currentOrganization, navigate] + [currentOrganization, navigate, showSnackbar] ); const handleDeleteApiKey = async () => {