diff --git a/docs/rest-apis/devportal/api-content.md b/docs/rest-apis/devportal/api-content.md index db9f293284..3d35186ae0 100644 --- a/docs/rest-apis/devportal/api-content.md +++ b/docs/rest-apis/devportal/api-content.md @@ -21,7 +21,7 @@ curl -X POST https://localhost:3000/api/v0.9/apis/{apiId}/assets \ Uploads the static content package for an API. -The `apiContent` ZIP must contain at least one of these root directories: +The `content` ZIP must contain at least one of these root directories: - `web/` for API landing-page assets such as markdown, HTML, CSS, JavaScript, and images. - `docs/` for downloadable API documents. @@ -31,7 +31,7 @@ Use `imageMetadata` to map uploaded images to API image roles such as the API ic > Payload ```yaml -apiContent: string +content: string docMetadata: '[{"name":"External guide","url":"https://example.com/docs/guide","type":"LINK"}]' imageMetadata: '{"api-icon":"icon.png"}' @@ -50,7 +50,7 @@ This operation requires Basic Auth authentication. |Name|In|Type|Required|Description| |---|---|---|---|---| |body|body|object|true|API content ZIP upload.| -|» apiContent|body|string(binary)|true|ZIP upload field named `apiContent`.| +|» content|body|string(binary)|true|ZIP upload field named `content`.| |» docMetadata|body|string|false|Optional JSON string containing API document link metadata.| |» imageMetadata|body|string|false|Optional JSON string containing API image metadata.| |apiId|path|string|true|The API's handle (unique per org). Resolves only to REST/SOAP/WS/WebSub/GraphQL APIs — MCP servers are addressed via `/mcp-servers`.| @@ -76,23 +76,7 @@ At least one of `web/` or `docs/` must exist at the ZIP root. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -133,7 +117,7 @@ At least one of `web/` or `docs/` must exist at the ZIP root. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|JSON message response.|[MessageResponse](schemas.md#schemamessageresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -144,7 +128,6 @@ At least one of `web/` or `docs/` must exist at the ZIP root. |Property|Value| |---|---| |status|error| -|status|error| ## Replace API content @@ -174,7 +157,7 @@ Image metadata is updated only when image metadata can be resolved from the uplo > Payload ```yaml -apiContent: string +content: string docMetadata: '[{"name":"External guide","url":"https://example.com/docs/guide","type":"LINK"}]' imageMetadata: '{"api-icon":"icon.png"}' @@ -193,7 +176,7 @@ This operation requires Basic Auth authentication. |Name|In|Type|Required|Description| |---|---|---|---|---| |body|body|object|true|API content ZIP upload.| -|» apiContent|body|string(binary)|true|ZIP upload field named `apiContent`.| +|» content|body|string(binary)|true|ZIP upload field named `content`.| |» docMetadata|body|string|false|Optional JSON string containing API document link metadata.| |» imageMetadata|body|string|false|Optional JSON string containing API image metadata.| |apiId|path|string|true|The API's handle (unique per org). Resolves only to REST/SOAP/WS/WebSub/GraphQL APIs — MCP servers are addressed via `/mcp-servers`.| @@ -219,23 +202,7 @@ At least one of `web/` or `docs/` must exist at the ZIP root. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -276,7 +243,7 @@ At least one of `web/` or `docs/` must exist at the ZIP root. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|JSON message response.|[MessageResponse](schemas.md#schemamessageresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -287,7 +254,6 @@ At least one of `web/` or `docs/` must exist at the ZIP root. |Property|Value| |---|---| |status|error| -|status|error| ## Get an API content file @@ -345,23 +311,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -398,7 +348,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Stored API content asset. The concrete media type depends on the stored file extension or whether the content is an external document link.|string| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Plain text success response.|string| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -409,7 +359,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Delete API content files @@ -450,23 +399,7 @@ This operation requires Basic Auth authentication. > Example responses -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -503,7 +436,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|API content deleted successfully.|None| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Plain text success response.|string| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -514,4 +447,3 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| diff --git a/docs/rest-apis/devportal/api-keys.md b/docs/rest-apis/devportal/api-keys.md index 3f47eac153..98a3d90a79 100644 --- a/docs/rest-apis/devportal/api-keys.md +++ b/docs/rest-apis/devportal/api-keys.md @@ -59,13 +59,19 @@ This operation requires Basic Auth authentication. } ``` -> 400 Response +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { - "code": "400", - "message": "Bad Request", - "description": "Invalid request parameters" + "status": "error", + "code": "MISSING_REQUIRED_PARAMETER", + "message": "Missing required parameter." +} +``` + +```json +{ + "message": "Missing or invalid fields in the request payload" } ``` @@ -73,8 +79,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "403", - "message": "Read-only mode" + "status": "error", + "code": "FORBIDDEN", + "message": "Write operations are disabled in read-only mode." } ``` @@ -82,9 +89,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -103,11 +110,19 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Generated API key. The plaintext `key` is returned exactly once.|[ApiKeyResponse](schemas.md#schemaapikeyresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode (e.g. read-only mode).|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode or caller permissions.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| +

Response Schema

+ +#### Enumerated Values + +|Property|Value| +|---|---| +|status|error| + ### Response Headers |Status|Header|Type|Format|Description| @@ -169,6 +184,7 @@ This operation requires Basic Auth authentication. "revokedAt": "2019-08-24T14:15:22Z" } ], + "count": 1, "pagination": { "total": 42, "limit": 20, @@ -177,23 +193,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -224,7 +224,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of API key metadata records.|Inline| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)|

Response Schema

@@ -244,6 +244,7 @@ Status Code **200** |»» expiresAt|string(date-time)¦null|false|none|none| |»» createdAt|string(date-time)|false|none|none| |»» revokedAt|string(date-time)¦null|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| @@ -261,7 +262,6 @@ Status Code **200** |Property|Value| |---|---| |status|error| -|status|error| ## Regenerate an API key @@ -330,8 +330,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "403", - "message": "Read-only mode" + "status": "error", + "code": "FORBIDDEN", + "message": "Write operations are disabled in read-only mode." } ``` @@ -339,9 +340,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -349,7 +350,8 @@ This operation requires Basic Auth authentication. ```json { - "code": "409", + "status": "error", + "code": "CONFLICT", "message": "Cannot regenerate a revoked key" } ``` @@ -369,9 +371,9 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Generated or regenerated API key. The plaintext `key` is returned exactly once.|[ApiKeyResponse](schemas.md#schemaapikeyresponse)| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode (e.g. read-only mode).|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The key has already been revoked and cannot be regenerated.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode or caller permissions.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The key has already been revoked and cannot be regenerated.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| ## Revoke an API key @@ -424,8 +426,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "403", - "message": "Read-only mode" + "status": "error", + "code": "FORBIDDEN", + "message": "Write operations are disabled in read-only mode." } ``` @@ -433,9 +436,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -443,7 +446,8 @@ This operation requires Basic Auth authentication. ```json { - "code": "409", + "status": "error", + "code": "CONFLICT", "message": "Key already revoked or not found" } ``` @@ -463,9 +467,9 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|API key revoked successfully.|None| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode (e.g. read-only mode).|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The key has already been revoked.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode or caller permissions.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The key has already been revoked.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| ## Associate an API key with an application @@ -528,13 +532,19 @@ This operation requires Basic Auth authentication. } ``` -> 400 Response +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { - "code": "400", - "message": "Bad Request", - "description": "Invalid request parameters" + "status": "error", + "code": "MISSING_REQUIRED_PARAMETER", + "message": "Missing required parameter." +} +``` + +```json +{ + "message": "Missing or invalid fields in the request payload" } ``` @@ -542,8 +552,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "403", - "message": "Read-only mode" + "status": "error", + "code": "FORBIDDEN", + "message": "Write operations are disabled in read-only mode." } ``` @@ -551,9 +562,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -561,7 +572,8 @@ This operation requires Basic Auth authentication. ```json { - "code": "409", + "status": "error", + "code": "CONFLICT", "message": "Cannot associate a revoked key" } ``` @@ -581,12 +593,20 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Association updated.|[ApiKeyApplicationResponse](schemas.md#schemaapikeyapplicationresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode (e.g. read-only mode).|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The key has already been revoked and cannot be associated with an application.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode or caller permissions.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The key has already been revoked and cannot be associated with an application.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| +

Response Schema

+ +#### Enumerated Values + +|Property|Value| +|---|---| +|status|error| + ## Remove an API key's application association @@ -637,8 +657,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "403", - "message": "Read-only mode" + "status": "error", + "code": "FORBIDDEN", + "message": "Write operations are disabled in read-only mode." } ``` @@ -646,9 +667,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -667,8 +688,8 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Association removed (or none existed).|None| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode (e.g. read-only mode).|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode or caller permissions.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| ## List API keys associated with an application @@ -725,6 +746,7 @@ This operation requires Basic Auth authentication. "revokedAt": "2019-08-24T14:15:22Z" } ], + "count": 1, "pagination": { "total": 42, "limit": 20, @@ -737,9 +759,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -758,7 +780,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of API key metadata records.|Inline| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)|

Response Schema

@@ -778,6 +800,7 @@ Status Code **200** |»» expiresAt|string(date-time)¦null|false|none|none| |»» createdAt|string(date-time)|false|none|none| |»» revokedAt|string(date-time)¦null|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| diff --git a/docs/rest-apis/devportal/api-workflows.md b/docs/rest-apis/devportal/api-workflows.md index 9568839ee8..f3a430b412 100644 --- a/docs/rest-apis/devportal/api-workflows.md +++ b/docs/rest-apis/devportal/api-workflows.md @@ -140,6 +140,7 @@ This operation requires Basic Auth authentication. "updatedBy": "string" } ], + "count": 1, "pagination": { "total": 42, "limit": 20, @@ -175,6 +176,7 @@ Status Code **200** |»» updatedAt|string¦null|false|none|none| |»» createdBy|string¦null|false|none|none| |»» updatedBy|string¦null|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| diff --git a/docs/rest-apis/devportal/apis.md b/docs/rest-apis/devportal/apis.md index f6afbbb628..38fa9fe0c5 100644 --- a/docs/rest-apis/devportal/apis.md +++ b/docs/rest-apis/devportal/apis.md @@ -19,18 +19,16 @@ curl -X POST https://localhost:3000/api/v0.9/apis \ ``` -Creates Developer Portal API metadata from either a full API artifact ZIP, an API metadata YAML file (`api.yaml` / `devportal.yaml` / `mcp.yaml`), or an `apiMetadata` JSON string. An API definition file is required unless supplied by the artifact ZIP. The YAML `spec` block accepts: `displayName`, `version`, `description`, `type`, `status`, `agentVisibility`, `tags`, `labels`, `referenceId`, `endpoints` (sandboxUrl, productionUrl), `businessInformation` (owners), and `subscriptionPlans`. The service also stores labels, subscription plan mappings, image metadata, and schema definitions for GraphQL APIs when provided. Via the JSON `apiMetadata` field, `type` is required — an omitted type is rejected with `400` (via YAML, an omitted `spec.type` defaults to `REST`). MCP servers must be created via `POST /api/v0.9/mcp-servers` instead — a request whose resolved `type` is `MCP` is rejected with `400`. -`subscriptionPlans` links existing org-level plans to this API by name — it does not create plans. In YAML it is a string array (`["Gold", "Silver"]`). In the JSON `apiMetadata` field it is an object array where only `id` is used (`[{"id":"Gold"}]`); extra fields such as `planId`, `displayName`, or `requestCount` are ignored. +Creates Developer Portal API metadata from either a full API artifact ZIP, an API metadata YAML file (`api.yaml` / `devportal.yaml` / `mcp.yaml`), or a `metadata` JSON string. An API definition file is required unless supplied by the artifact ZIP. The YAML `spec` block accepts: `displayName`, `version`, `description`, `type`, `status`, `agentVisibility`, `tags`, `labels`, `referenceId`, `endpoints` (sandboxUrl, productionUrl), `businessInformation` (owners), and `subscriptionPlans`. The service also stores labels, subscription plan mappings, image metadata, and schema definitions for GraphQL APIs when provided. Via the JSON `metadata` field, `type` is required — an omitted type is rejected with `400` (via YAML, an omitted `spec.type` defaults to `REST`). MCP servers must be created via `POST /api/v0.9/mcp-servers` instead — a request whose resolved `type` is `MCP` is rejected with `400`. +`subscriptionPlans` links existing org-level plans to this API by name — it does not create plans. In YAML it is a string array (`["Gold", "Silver"]`). In the JSON `metadata` field it is an object array where only `id` is used (`[{"id":"Gold"}]`); extra fields such as `planId`, `displayName`, or `requestCount` are ignored. > Payload ```yaml -api: string -apiDefinition: string +definition: string artifact: string -schemaDefinition: string -apiMetadata: '{"name":"Weather API","version":"v1","description":"Weather - forecast API","type":"REST","agentVisibility":"VISIBLE", +metadata: '{"name":"Weather API","version":"v1","description":"Weather forecast + API","type":"REST","agentVisibility":"VISIBLE", "status":"PUBLISHED","tags":["weather"],"labels":["default"],"endPoints":{ "productionURL":"https://api.example.com/weather", "sandboxURL":"https://sandbox.example.com/weather"},"subscriptionPlans":[{"id":"Gold"}]}' @@ -48,12 +46,10 @@ This operation requires Basic Auth authentication. |Name|In|Type|Required|Description| |---|---|---|---|---| -|body|body|object|true|API metadata upload. Send either `artifact`, or `api` with `apiDefinition`, or `apiMetadata` with `apiDefinition`. `schemaDefinition` carries a GraphQL SDL schema. (MCP servers are created via `/mcp-servers` with the dedicated `McpServerMultipartBody`, not this body.)| -|» api|body|string(binary)|false|API metadata YAML file.| -|» apiDefinition|body|string(binary)|false|API definition file.| +|body|body|object|true|API metadata upload. Send either `artifact`, or `metadata` with `definition`. For a GraphQL API the `definition` field carries the SDL schema. (MCP servers are created via `/mcp-servers` with the dedicated `McpServerMultipartBody`, not this body.)| +|» definition|body|string(binary)|false|API definition file. For REST/SOAP/etc. this is the OpenAPI/WSDL/AsyncAPI contract; for a GraphQL API it is the SDL schema.| |» artifact|body|string(binary)|false|Full API ZIP artifact containing metadata and definition files.| -|» schemaDefinition|body|string(binary)|false|GraphQL SDL schema definition file.| -|» apiMetadata|body|string|false|JSON string accepted by the service when the `api` YAML file is not supplied. Accepted top-level fields: `name`, `version`, `description`, `type`, `agentVisibility`, `status`, `referenceId`, `id`, `tags`, `labels`, `owners`, `endPoints` (productionURL, sandboxURL), and `subscriptionPlans` (array of `{ id }` objects — only `id` is read; the plan must already exist in the organization). `id` becomes the API's stored handle; when the API is created from a YAML artifact instead, the handle is always taken from `metadata.name`.| +|» metadata|body|string|false|API metadata, supplied either as a JSON string field or as an uploaded YAML/JSON file (a k8s-style document with `kind`, `metadata.name`, and a `spec` block; file names `metadata.yaml`/`.yml`/`.json`, or the legacy `api.yaml`/`mcp.yaml`/`devportal.yaml`). As a JSON string it accepts these top-level fields: `name`, `version`, `description`, `type`, `agentVisibility`, `status`, `referenceId`, `id`, `tags`, `labels`, `owners`, `endPoints` (productionURL, sandboxURL), and `subscriptionPlans` (array of `{ id }` objects — only `id` is read; the plan must already exist in the organization). `id` becomes the API's stored handle; when the API is created from a YAML artifact instead, the handle is always taken from `metadata.name`.| > Example responses @@ -64,7 +60,7 @@ This operation requires Basic Auth authentication. "id": "weather-api-v1", "refId": "cp-api-12345", "name": "Weather API", - "apiTitle": "Weather Forecast API", + "title": "Weather Forecast API", "version": "v1", "status": "PUBLISHED", "description": "Weather forecast API.", @@ -88,23 +84,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -155,7 +135,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Created API metadata payload returned by the service.|[ApiMetadataCreateResponse](schemas.md#schemaapimetadatacreateresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -167,7 +147,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ### Response Headers @@ -206,7 +185,7 @@ This operation requires Basic Auth authentication. |Name|In|Type|Required|Description| |---|---|---|---|---| |query|query|string|false|Free-text API metadata search term.| -|apiName|query|string|false|Exact API name filter.| +|name|query|string|false|Exact API name filter.| |version|query|string|false|Exact API version filter.| |tags|query|string|false|Comma-separated tag names. Matches APIs tagged with any of the given names.| |view|query|string|false|Developer Portal view name used to filter visible APIs.| @@ -238,6 +217,7 @@ This operation requires Basic Auth authentication. } } ], + "count": 1, "pagination": { "total": 1, "limit": 20, @@ -246,23 +226,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -293,7 +257,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of API metadata DTOs.|Inline| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)|

Response Schema

@@ -310,7 +274,7 @@ Status Code **200** |---|---|---|---|---| |»» *anonymous*|[ApiInfoResponse](schemas.md#schemaapiinforesponse)|false|none|Fields are returned at the root of ApiMetadataResponse / ApiMetadataCreateResponse (not nested under an `apiInfo` key) — this schema exists only to share the field set between the two via `allOf`.| |»»» name|string|false|none|none| -|»»» apiTitle|string¦null|false|none|none| +|»»» title|string¦null|false|none|none| |»»» remotes|[object]|false|none|none| |»»» version|string|false|none|none| |»»» status|string|false|none|API lifecycle status.| @@ -378,6 +342,7 @@ Status Code **200** |»»» updatedBy|string|false|none|Identity of the user who last updated this API, or `deleted_user` if that user's IDP reference no longer exists. Present on single-resource GET responses only, omitted on list items.| |»»» createdAt|string(date-time)|false|none|none| |»»» updatedAt|string(date-time)|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| @@ -412,7 +377,6 @@ Status Code **200** |Property|Value| |---|---| |status|error| -|status|error| ## Get API metadata @@ -455,7 +419,7 @@ This operation requires Basic Auth authentication. "id": "weather-api-v1", "refId": "cp-api-12345", "name": "Weather API", - "apiTitle": "Weather Forecast API", + "title": "Weather Forecast API", "remotes": [], "version": "v1", "status": "PUBLISHED", @@ -481,23 +445,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -534,7 +482,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API metadata DTO returned by the service.|[ApiMetadataResponse](schemas.md#schemaapimetadataresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Plain text success response.|string| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -545,7 +493,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Update API metadata @@ -566,17 +513,15 @@ curl -X PUT https://localhost:3000/api/v0.9/apis/{apiId} \ ``` -Updates Developer Portal API metadata and its stored definition. Accepts the same YAML spec fields and `apiMetadata` JSON format as the create operation. The update flow can also adjust label mappings, subscription plan mappings, schema definitions, and image metadata. Status changes to unpublished are rejected when active subscriptions exist. `type` is required (see the create operation) and is immutable — it must match the API's existing type; a different value is rejected with `409`. +Updates Developer Portal API metadata and its stored definition. Accepts the same YAML spec fields and `metadata` JSON format as the create operation. The update flow can also adjust label mappings, subscription plan mappings, schema definitions, and image metadata. Status changes to unpublished are rejected when active subscriptions exist. `type` is required (see the create operation) and is immutable — it must match the API's existing type; a different value is rejected with `409`. > Payload ```yaml -api: string -apiDefinition: string +definition: string artifact: string -schemaDefinition: string -apiMetadata: '{"name":"Weather API","version":"v1","description":"Weather - forecast API","type":"REST","agentVisibility":"VISIBLE", +metadata: '{"name":"Weather API","version":"v1","description":"Weather forecast + API","type":"REST","agentVisibility":"VISIBLE", "status":"PUBLISHED","tags":["weather"],"labels":["default"],"endPoints":{ "productionURL":"https://api.example.com/weather", "sandboxURL":"https://sandbox.example.com/weather"},"subscriptionPlans":[{"id":"Gold"}]}' @@ -594,12 +539,10 @@ This operation requires Basic Auth authentication. |Name|In|Type|Required|Description| |---|---|---|---|---| -|body|body|object|true|API metadata upload. Send either `artifact`, or `api` with `apiDefinition`, or `apiMetadata` with `apiDefinition`. `schemaDefinition` carries a GraphQL SDL schema. (MCP servers are created via `/mcp-servers` with the dedicated `McpServerMultipartBody`, not this body.)| -|» api|body|string(binary)|false|API metadata YAML file.| -|» apiDefinition|body|string(binary)|false|API definition file.| +|body|body|object|true|API metadata upload. Send either `artifact`, or `metadata` with `definition`. For a GraphQL API the `definition` field carries the SDL schema. (MCP servers are created via `/mcp-servers` with the dedicated `McpServerMultipartBody`, not this body.)| +|» definition|body|string(binary)|false|API definition file. For REST/SOAP/etc. this is the OpenAPI/WSDL/AsyncAPI contract; for a GraphQL API it is the SDL schema.| |» artifact|body|string(binary)|false|Full API ZIP artifact containing metadata and definition files.| -|» schemaDefinition|body|string(binary)|false|GraphQL SDL schema definition file.| -|» apiMetadata|body|string|false|JSON string accepted by the service when the `api` YAML file is not supplied. Accepted top-level fields: `name`, `version`, `description`, `type`, `agentVisibility`, `status`, `referenceId`, `id`, `tags`, `labels`, `owners`, `endPoints` (productionURL, sandboxURL), and `subscriptionPlans` (array of `{ id }` objects — only `id` is read; the plan must already exist in the organization). `id` becomes the API's stored handle; when the API is created from a YAML artifact instead, the handle is always taken from `metadata.name`.| +|» metadata|body|string|false|API metadata, supplied either as a JSON string field or as an uploaded YAML/JSON file (a k8s-style document with `kind`, `metadata.name`, and a `spec` block; file names `metadata.yaml`/`.yml`/`.json`, or the legacy `api.yaml`/`mcp.yaml`/`devportal.yaml`). As a JSON string it accepts these top-level fields: `name`, `version`, `description`, `type`, `agentVisibility`, `status`, `referenceId`, `id`, `tags`, `labels`, `owners`, `endPoints` (productionURL, sandboxURL), and `subscriptionPlans` (array of `{ id }` objects — only `id` is read; the plan must already exist in the organization). `id` becomes the API's stored handle; when the API is created from a YAML artifact instead, the handle is always taken from `metadata.name`.| |apiId|path|string|true|The API's handle (unique per org). Resolves only to REST/SOAP/WS/WebSub/GraphQL APIs — MCP servers are addressed via `/mcp-servers`.| > Example responses @@ -611,7 +554,7 @@ This operation requires Basic Auth authentication. "id": "weather-api-v1", "refId": "cp-api-12345", "name": "Weather API", - "apiTitle": "Weather Forecast API", + "title": "Weather Forecast API", "remotes": [], "version": "v1", "status": "PUBLISHED", @@ -637,23 +580,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -704,7 +631,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API metadata DTO returned by the service.|[ApiMetadataResponse](schemas.md#schemaapimetadataresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -716,7 +643,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Delete API metadata @@ -758,23 +684,7 @@ This operation requires Basic Auth authentication. "string" ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -825,7 +735,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Plain text success response.|string| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -837,4 +747,3 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| diff --git a/docs/rest-apis/devportal/application-keys.md b/docs/rest-apis/devportal/application-keys.md index 6e11a88267..99b6a6b094 100644 --- a/docs/rest-apis/devportal/application-keys.md +++ b/docs/rest-apis/devportal/application-keys.md @@ -59,23 +59,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -126,7 +110,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Application OAuth key mapping response.|[ApplicationOAuthKeyResponse](schemas.md#schemaapplicationoauthkeyresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -138,7 +122,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Generate an OAuth access token @@ -202,23 +185,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -259,7 +226,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|OAuth access token response.|[OAuthTokenResponse](schemas.md#schemaoauthtokenresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -270,7 +237,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Remove an OAuth client_id mapping diff --git a/docs/rest-apis/devportal/applications.md b/docs/rest-apis/devportal/applications.md index 95047f25c4..82973b381c 100644 --- a/docs/rest-apis/devportal/applications.md +++ b/docs/rest-apis/devportal/applications.md @@ -53,6 +53,7 @@ This operation requires Basic Auth authentication. ] } ], + "count": 1, "pagination": { "total": 1, "limit": 20, @@ -96,6 +97,7 @@ Status Code **200** |»» updatedBy|string|false|none|Identity of the user who last updated this application, or `deleted_user` if that user's IDP reference no longer exists. Present on single-resource GET responses only, omitted on list items.| |»» createdAt|string(date-time)|false|none|none| |»» updatedAt|string(date-time)|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| @@ -172,23 +174,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -229,7 +215,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Application DTO.|[ApplicationResponse](schemas.md#schemaapplicationresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -240,7 +226,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ### Response Headers @@ -394,23 +379,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -461,7 +430,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Application DTO.|[ApplicationResponse](schemas.md#schemaapplicationresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -473,7 +442,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Delete an application diff --git a/docs/rest-apis/devportal/authentication.md b/docs/rest-apis/devportal/authentication.md index 7e04a9f922..7df452e211 100644 --- a/docs/rest-apis/devportal/authentication.md +++ b/docs/rest-apis/devportal/authentication.md @@ -9,83 +9,90 @@ |Scope|Scope Description| |---|---| |dp:org_read|Read organizations.| -|dp:org_write|Create or update organizations.| +|dp:org_create|Create organizations.| +|dp:org_update|Update organizations.| |dp:org_delete|Delete organizations.| |dp:org_manage|Manage organizations (including creating, updating, and deleting).| -|dp:idp_read|Read organization identity provider configuration.| -|dp:idp_write|Create or update identity provider configuration.| -|dp:idp_delete|Delete identity provider configuration.| -|dp:idp_manage|Manage organization identity provider configuration.| -|dp:provider_read|Read providers.| -|dp:provider_write|Create or update providers.| -|dp:provider_delete|Delete providers.| -|dp:provider_manage|Manage providers (including creating, updating, and deleting).| -|dp:org_content_read|Read organization layout or content.| -|dp:org_content_write|Create or update organization layout or content.| -|dp:org_content_delete|Delete organization layout or content.| -|dp:org_content_manage|Manage organization layout or content.| +|dp:org_content_read|Read organization theme assets.| +|dp:org_content_manage|Apply or reset organization theme.| |dp:api_read|Read API metadata.| -|dp:api_write|Create or update API metadata.| +|dp:api_create|Create API metadata.| +|dp:api_update|Update API metadata.| |dp:api_delete|Delete API metadata.| |dp:api_manage|Manage API metadata.| |dp:api_content_read|Read API content.| -|dp:api_content_write|Create or update API content.| +|dp:api_content_create|Create API content.| +|dp:api_content_update|Update API content.| |dp:api_content_delete|Delete API content.| |dp:api_content_manage|Manage API content.| -|dp:sub_policy_read|Read subscription policies.| -|dp:sub_policy_write|Create or update subscription policies.| -|dp:sub_policy_delete|Delete subscription policies.| -|dp:sub_policy_manage|Manage subscription policies.| +|dp:mcp_read|Read MCP server metadata.| +|dp:mcp_create|Create MCP server metadata.| +|dp:mcp_update|Update MCP server metadata.| +|dp:mcp_delete|Delete MCP server metadata.| +|dp:mcp_manage|Manage MCP server metadata.| +|dp:mcp_content_read|Read MCP server content.| +|dp:mcp_content_create|Create MCP server content.| +|dp:mcp_content_update|Update MCP server content.| +|dp:mcp_content_delete|Delete MCP server content.| +|dp:mcp_content_manage|Manage MCP server content.| +|dp:mcp_key_read|Read MCP server API keys.| +|dp:mcp_key_create|Generate MCP server API keys.| +|dp:mcp_key_update|Regenerate MCP server API keys.| +|dp:mcp_key_revoke|Revoke MCP server API keys.| +|dp:mcp_key_manage|Manage MCP server API keys.| +|dp:sub_plan_read|Read subscription plans.| +|dp:sub_plan_create|Create subscription plans.| +|dp:sub_plan_update|Update subscription plans.| +|dp:sub_plan_delete|Delete subscription plans.| +|dp:sub_plan_manage|Manage subscription plans.| |dp:label_read|Read labels.| -|dp:label_write|Create or update labels.| +|dp:label_create|Create labels.| +|dp:label_update|Update labels.| |dp:label_delete|Delete labels.| |dp:label_manage|Manage labels.| |dp:app_read|Read applications.| -|dp:app_write|Create or update applications.| +|dp:app_create|Create applications.| +|dp:app_update|Update applications.| |dp:app_delete|Delete applications.| -|dp:app_import|Import applications.| |dp:app_manage|Manage applications.| |dp:subscription_read|Read subscriptions.| -|dp:subscription_write|Create or update subscriptions.| +|dp:subscription_create|Create subscriptions.| +|dp:subscription_update|Update subscriptions.| |dp:subscription_delete|Delete subscriptions.| |dp:subscription_manage|Manage subscriptions.| |dp:api_key_read|Read API keys.| -|dp:api_key_write|Generate or regenerate API keys.| +|dp:api_key_create|Generate API keys.| +|dp:api_key_update|Regenerate API keys.| |dp:api_key_revoke|Revoke API keys.| |dp:api_key_manage|Manage API keys.| |dp:app_key_mapping_read|Read application key mappings.| -|dp:app_key_mapping_write|Create application key mappings.| +|dp:app_key_mapping_create|Create application key mappings.| |dp:app_key_mapping_manage|Manage application key mappings.| |dp:view_read|Read views.| -|dp:view_write|Create or update views.| +|dp:view_create|Create views.| +|dp:view_update|Update views.| |dp:view_delete|Delete views.| |dp:view_manage|Manage views.| -|dp:app_key_write|Generate, update, regenerate, or clean up application keys.| +|dp:app_key_create|Generate and create application keys.| +|dp:app_key_update|Update application keys.| |dp:app_key_revoke|Revoke application keys.| |dp:app_key_manage|Manage application keys.| -|dp:billing_read|Read billing information, usage data, payment methods, and subscription billing status.| -|dp:billing_write|Create checkout sessions, register checkout sessions, create billing portal sessions, or cancel paid subscriptions.| -|dp:billing_manage|Manage billing information, checkout, portal, and subscription billing actions.| -|dp:billing_config_read|Read billing engine key configuration.| -|dp:billing_config_write|Create or update billing engine key configuration.| -|dp:billing_config_delete|Delete billing engine key configuration.| -|dp:billing_config_manage|Manage billing engine key configuration.| -|dp:usage_read|Read subscription usage.| -|dp:usage_manage|Manage subscription usage access.| -|dp:invoice_read|Read invoices and invoice links.| -|dp:invoice_manage|Manage invoice access.| -|dp:api_flow_read|Read API flows.| -|dp:api_flow_write|Create, update, or generate API flows.| -|dp:api_flow_delete|Delete API flows.| -|dp:api_flow_manage|Manage API flows.| +|dp:api_workflow_read|Read API workflows.| +|dp:api_workflow_create|Create or generate API workflows.| +|dp:api_workflow_update|Update API workflows.| +|dp:api_workflow_delete|Delete API workflows.| +|dp:api_workflow_manage|Manage API workflows.| |dp:event_read|Read webhook events and delivery details.| -|dp:delivery_manage|Retry failed webhook deliveries.| -|dp:utility_write|Create temporary utility files.| -|dp:utility_manage|Manage utility operations.| |dp:km_read|Read key manager configurations.| -|dp:km_write|Create or update key manager configurations.| +|dp:km_create|Create key manager configurations.| +|dp:km_update|Update key manager configurations.| |dp:km_delete|Delete key manager configurations.| |dp:km_manage|Manage key manager configurations (including creating, updating, and deleting).| +|dp:webhook_subscriber_read|Read webhook subscriber configurations.| +|dp:webhook_subscriber_create|Create webhook subscriber configurations.| +|dp:webhook_subscriber_update|Update webhook subscriber configurations.| +|dp:webhook_subscriber_delete|Delete webhook subscriber configurations.| +|dp:webhook_subscriber_manage|Manage webhook subscriber configurations (including creating, updating, and deleting).| * API Key (apiKeyAuth) - Parameter Name: **x-api-key**, in: header. API key authentication. Server-side authorization should bind each key to the same fine-grained permissions used by OAuth2 scopes. diff --git a/docs/rest-apis/devportal/key-managers.md b/docs/rest-apis/devportal/key-managers.md index cf6962245b..d52bc09b41 100644 --- a/docs/rest-apis/devportal/key-managers.md +++ b/docs/rest-apis/devportal/key-managers.md @@ -72,23 +72,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -129,7 +113,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Key manager configuration response.|[KeyManagerResponseSchema](schemas.md#schemakeymanagerresponseschema)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -140,7 +124,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ### Response Headers @@ -200,6 +183,7 @@ This operation requires Basic Auth authentication. "updatedAt": "2026-05-07T08:30:00Z" } ], + "count": 1, "pagination": { "total": 42, "limit": 20, @@ -263,6 +247,7 @@ Status Code **200** |Name|Type|Required|Restrictions|Description| |---|---|---|---|---| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| @@ -427,23 +412,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -494,7 +463,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Key manager configuration response.|[KeyManagerResponseSchema](schemas.md#schemakeymanagerresponseschema)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -506,7 +475,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Delete a key manager diff --git a/docs/rest-apis/devportal/labels.md b/docs/rest-apis/devportal/labels.md index 3cecf7ae18..b7ebf5e724 100644 --- a/docs/rest-apis/devportal/labels.md +++ b/docs/rest-apis/devportal/labels.md @@ -54,23 +54,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -111,7 +95,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|The created label.|[LabelResponse](schemas.md#schemalabelresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -122,7 +106,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ### Response Headers @@ -168,6 +151,7 @@ This operation requires Basic Auth authentication. "displayName": "Premium APIs" } ], + "count": 1, "pagination": { "total": 42, "limit": 20, @@ -176,23 +160,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -223,7 +191,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Paginated list of label DTOs.|Inline| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)|

Response Schema

@@ -235,6 +203,7 @@ Status Code **200** |» list|[[LabelResponse](schemas.md#schemalabelresponse)]|false|none|none| |»» id|string|false|none|The label's handle (unique per org). Not the internal database uuid.| |»» displayName|string|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| @@ -245,7 +214,6 @@ Status Code **200** |Property|Value| |---|---| |status|error| -|status|error| ## Get a label @@ -373,23 +341,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -440,7 +392,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Label DTO.|[LabelResponse](schemas.md#schemalabelresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -452,7 +404,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Delete a label diff --git a/docs/rest-apis/devportal/mcp-server-content.md b/docs/rest-apis/devportal/mcp-server-content.md index f21c54a9a2..d48cd2cf04 100644 --- a/docs/rest-apis/devportal/mcp-server-content.md +++ b/docs/rest-apis/devportal/mcp-server-content.md @@ -24,7 +24,7 @@ Uploads the static content package for an MCP server. Mirrors `POST /api/v0.9/ap > Payload ```yaml -apiContent: string +content: string docMetadata: '[{"name":"External guide","url":"https://example.com/docs/guide","type":"LINK"}]' imageMetadata: '{"api-icon":"icon.png"}' @@ -43,7 +43,7 @@ This operation requires Basic Auth authentication. |Name|In|Type|Required|Description| |---|---|---|---|---| |body|body|object|true|API content ZIP upload.| -|» apiContent|body|string(binary)|true|ZIP upload field named `apiContent`.| +|» content|body|string(binary)|true|ZIP upload field named `content`.| |» docMetadata|body|string|false|Optional JSON string containing API document link metadata.| |» imageMetadata|body|string|false|Optional JSON string containing API image metadata.| |mcpServerId|path|string|true|The MCP server's handle (unique per org).| @@ -69,23 +69,7 @@ At least one of `web/` or `docs/` must exist at the ZIP root. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -126,7 +110,7 @@ At least one of `web/` or `docs/` must exist at the ZIP root. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|JSON message response.|[MessageResponse](schemas.md#schemamessageresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -137,7 +121,6 @@ At least one of `web/` or `docs/` must exist at the ZIP root. |Property|Value| |---|---| |status|error| -|status|error| ## Replace MCP server content @@ -163,7 +146,7 @@ Replaces or adds static content files for an existing MCP server. Mirrors `PUT / > Payload ```yaml -apiContent: string +content: string docMetadata: '[{"name":"External guide","url":"https://example.com/docs/guide","type":"LINK"}]' imageMetadata: '{"api-icon":"icon.png"}' @@ -182,7 +165,7 @@ This operation requires Basic Auth authentication. |Name|In|Type|Required|Description| |---|---|---|---|---| |body|body|object|true|API content ZIP upload.| -|» apiContent|body|string(binary)|true|ZIP upload field named `apiContent`.| +|» content|body|string(binary)|true|ZIP upload field named `content`.| |» docMetadata|body|string|false|Optional JSON string containing API document link metadata.| |» imageMetadata|body|string|false|Optional JSON string containing API image metadata.| |mcpServerId|path|string|true|The MCP server's handle (unique per org).| @@ -208,23 +191,7 @@ At least one of `web/` or `docs/` must exist at the ZIP root. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -265,7 +232,7 @@ At least one of `web/` or `docs/` must exist at the ZIP root. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|JSON message response.|[MessageResponse](schemas.md#schemamessageresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -276,7 +243,6 @@ At least one of `web/` or `docs/` must exist at the ZIP root. |Property|Value| |---|---| |status|error| -|status|error| ## Get an MCP server content file @@ -330,23 +296,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -383,7 +333,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Stored API content asset. The concrete media type depends on the stored file extension or whether the content is an external document link.|string| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Plain text success response.|string| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -394,7 +344,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Delete MCP server content files @@ -432,23 +381,7 @@ This operation requires Basic Auth authentication. > Example responses -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -485,7 +418,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|MCP server content deleted successfully.|None| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Plain text success response.|string| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -496,4 +429,3 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| diff --git a/docs/rest-apis/devportal/mcp-server-keys.md b/docs/rest-apis/devportal/mcp-server-keys.md index 0f7856477e..c9da636293 100644 --- a/docs/rest-apis/devportal/mcp-server-keys.md +++ b/docs/rest-apis/devportal/mcp-server-keys.md @@ -59,13 +59,19 @@ This operation requires Basic Auth authentication. } ``` -> 400 Response +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { - "code": "400", - "message": "Bad Request", - "description": "Invalid request parameters" + "status": "error", + "code": "MISSING_REQUIRED_PARAMETER", + "message": "Missing required parameter." +} +``` + +```json +{ + "message": "Missing or invalid fields in the request payload" } ``` @@ -73,8 +79,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "403", - "message": "Read-only mode" + "status": "error", + "code": "FORBIDDEN", + "message": "Write operations are disabled in read-only mode." } ``` @@ -82,9 +89,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -103,11 +110,19 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Generated API key. The plaintext `key` is returned exactly once.|[ApiKeyResponse](schemas.md#schemaapikeyresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode (e.g. read-only mode).|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode or caller permissions.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| +

Response Schema

+ +#### Enumerated Values + +|Property|Value| +|---|---| +|status|error| + ### Response Headers |Status|Header|Type|Format|Description| @@ -169,6 +184,7 @@ This operation requires Basic Auth authentication. "revokedAt": "2019-08-24T14:15:22Z" } ], + "count": 1, "pagination": { "total": 42, "limit": 20, @@ -177,23 +193,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -224,7 +224,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of API key metadata records.|Inline| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)|

Response Schema

@@ -244,6 +244,7 @@ Status Code **200** |»» expiresAt|string(date-time)¦null|false|none|none| |»» createdAt|string(date-time)|false|none|none| |»» revokedAt|string(date-time)¦null|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| @@ -261,7 +262,6 @@ Status Code **200** |Property|Value| |---|---| |status|error| -|status|error| ## Regenerate an MCP server API key @@ -330,8 +330,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "403", - "message": "Read-only mode" + "status": "error", + "code": "FORBIDDEN", + "message": "Write operations are disabled in read-only mode." } ``` @@ -339,9 +340,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -349,7 +350,8 @@ This operation requires Basic Auth authentication. ```json { - "code": "409", + "status": "error", + "code": "CONFLICT", "message": "Cannot regenerate a revoked key" } ``` @@ -369,9 +371,9 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Generated or regenerated API key. The plaintext `key` is returned exactly once.|[ApiKeyResponse](schemas.md#schemaapikeyresponse)| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode (e.g. read-only mode).|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The key has already been revoked and cannot be regenerated.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode or caller permissions.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The key has already been revoked and cannot be regenerated.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| ## Revoke an MCP server API key @@ -424,8 +426,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "403", - "message": "Read-only mode" + "status": "error", + "code": "FORBIDDEN", + "message": "Write operations are disabled in read-only mode." } ``` @@ -433,9 +436,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -443,7 +446,8 @@ This operation requires Basic Auth authentication. ```json { - "code": "409", + "status": "error", + "code": "CONFLICT", "message": "Key already revoked or not found" } ``` @@ -463,9 +467,9 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|API key revoked successfully.|None| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode (e.g. read-only mode).|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The key has already been revoked.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode or caller permissions.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The key has already been revoked.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| ## Associate an MCP server API key with an application @@ -528,13 +532,19 @@ This operation requires Basic Auth authentication. } ``` -> 400 Response +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { - "code": "400", - "message": "Bad Request", - "description": "Invalid request parameters" + "status": "error", + "code": "MISSING_REQUIRED_PARAMETER", + "message": "Missing required parameter." +} +``` + +```json +{ + "message": "Missing or invalid fields in the request payload" } ``` @@ -542,8 +552,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "403", - "message": "Read-only mode" + "status": "error", + "code": "FORBIDDEN", + "message": "Write operations are disabled in read-only mode." } ``` @@ -551,9 +562,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -561,7 +572,8 @@ This operation requires Basic Auth authentication. ```json { - "code": "409", + "status": "error", + "code": "CONFLICT", "message": "Cannot associate a revoked key" } ``` @@ -581,12 +593,20 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Association updated.|[ApiKeyApplicationResponse](schemas.md#schemaapikeyapplicationresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode (e.g. read-only mode).|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The key has already been revoked and cannot be associated with an application.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode or caller permissions.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The key has already been revoked and cannot be associated with an application.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| +

Response Schema

+ +#### Enumerated Values + +|Property|Value| +|---|---| +|status|error| + ## Remove an MCP server API key's application association @@ -637,8 +657,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "403", - "message": "Read-only mode" + "status": "error", + "code": "FORBIDDEN", + "message": "Write operations are disabled in read-only mode." } ``` @@ -646,9 +667,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -667,6 +688,6 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Association removed (or none existed).|None| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode (e.g. read-only mode).|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Request is forbidden for the current runtime mode or caller permissions.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| diff --git a/docs/rest-apis/devportal/mcp-servers.md b/docs/rest-apis/devportal/mcp-servers.md index 35507d20e6..cfb437a5ca 100644 --- a/docs/rest-apis/devportal/mcp-servers.md +++ b/docs/rest-apis/devportal/mcp-servers.md @@ -19,15 +19,14 @@ curl -X POST https://localhost:3000/api/v0.9/mcp-servers \ ``` -Creates Developer Portal MCP server metadata. Accepts the same metadata input formats as `POST /api/v0.9/apis` (artifact ZIP, `api.yaml` / `devportal.yaml` / `mcp.yaml`, or `apiMetadata` JSON), but the created record is always typed `MCP`. An MCP server's contract is its `schemaDefinition` — the tools, resources, and prompts it exposes — not an OpenAPI-style `apiDefinition`; a `schemaDefinition` is required and any `apiDefinition` sent is ignored. Via the JSON `apiMetadata` field, `type` must be explicitly `MCP`; an omitted type or any other value is rejected with a 400 (use `POST /api/v0.9/apis`). +Creates Developer Portal MCP server metadata. Accepts the same metadata input formats as `POST /api/v0.9/apis` (artifact ZIP, `api.yaml` / `devportal.yaml` / `mcp.yaml`, or `metadata` JSON), but the created record is always typed `MCP`. An MCP server's contract is its `definition` (tools schema) — the tools, resources, and prompts it exposes — not an OpenAPI-style API contract; a `definition` is required. Via the JSON `metadata` field, `type` must be explicitly `MCP`; an omitted type or any other value is rejected with a 400 (use `POST /api/v0.9/apis`). > Payload ```yaml -api: string artifact: string -schemaDefinition: string -apiMetadata: '{"name":"Travel Assistant MCP","version":"v1","description":"MCP +definition: string +metadata: '{"name":"Travel Assistant MCP","version":"v1","description":"MCP server for travel planning tools","type":"MCP","agentVisibility":"VISIBLE","status":"PUBLISHED", "tags":["mcp"],"labels":["default"],"endPoints":{"productionURL":"https://mcp.example.com", @@ -46,11 +45,10 @@ This operation requires Basic Auth authentication. |Name|In|Type|Required|Description| |---|---|---|---|---| -|body|body|object|true|MCP server upload. Provide the server metadata via `apiMetadata` (JSON), an `api` YAML file, or a full `artifact` ZIP, together with its `schemaDefinition` — the tools, resources, and prompts the server exposes. An MCP server has no OpenAPI-style `apiDefinition`; its `schemaDefinition` IS its contract, so it is required on create (and replaces the stored schema when supplied on update).| -|» api|body|string(binary)|false|MCP server metadata YAML file (kind MCP).| -|» artifact|body|string(binary)|false|Full MCP server ZIP artifact containing the metadata and the schemaDefinition.| -|» schemaDefinition|body|string(binary)|false|MCP tools schema (YAML or JSON) listing the tools, resources, and prompts the server exposes. Required on create; when supplied on update it replaces the stored schema.| -|» apiMetadata|body|string|false|JSON string accepted when the `api` YAML file is not supplied. Accepted top-level fields: `name`, `version`, `description`, `type` (must be `MCP`), `agentVisibility`, `status`, `referenceId`, `id`, `tags`, `labels`, `owners`, `endPoints` (productionURL, sandboxURL), and `subscriptionPlans` (array of `{ id }` objects — only `id` is read; the plan must already exist in the organization). `id` becomes the MCP server's stored handle.| +|body|body|object|true|MCP server upload. Provide the server metadata via `metadata` (a JSON string or an uploaded YAML/JSON file), or a full `artifact` ZIP, together with its `definition` — the tools, resources, and prompts the server exposes. An MCP server has no OpenAPI-style API contract; its `definition` (tools schema) IS its contract, so it is required on create (and replaces the stored schema when supplied on update).| +|» artifact|body|string(binary)|false|Full MCP server ZIP artifact containing the metadata and the definition.| +|» definition|body|string(binary)|false|MCP tools schema (YAML or JSON) listing the tools, resources, and prompts the server exposes. Required on create; when supplied on update it replaces the stored schema.| +|» metadata|body|string|false|MCP server metadata, supplied either as a JSON string field or as an uploaded YAML/JSON file (a k8s-style document of kind `MCP`; file names `metadata.yaml`/`.yml`/`.json`, or the legacy `api.yaml`/`mcp.yaml`/`devportal.yaml`). As a JSON string it accepts these top-level fields: `name`, `version`, `description`, `type` (must be `MCP`), `agentVisibility`, `status`, `referenceId`, `id`, `tags`, `labels`, `owners`, `endPoints` (productionURL, sandboxURL), and `subscriptionPlans` (array of `{ id }` objects — only `id` is read; the plan must already exist in the organization). `id` becomes the MCP server's stored handle.| > Example responses @@ -61,7 +59,7 @@ This operation requires Basic Auth authentication. "id": "weather-api-v1", "refId": "cp-api-12345", "name": "Weather API", - "apiTitle": "Weather Forecast API", + "title": "Weather Forecast API", "version": "v1", "status": "PUBLISHED", "description": "Weather forecast API.", @@ -85,23 +83,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -152,7 +134,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Created MCP server metadata payload returned by the service. Always typed `MCP`.|[ApiMetadataCreateResponse](schemas.md#schemaapimetadatacreateresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -164,7 +146,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ### Response Headers @@ -203,7 +184,7 @@ This operation requires Basic Auth authentication. |Name|In|Type|Required|Description| |---|---|---|---|---| |query|query|string|false|Free-text API metadata search term.| -|apiName|query|string|false|Exact API name filter.| +|name|query|string|false|Exact API name filter.| |version|query|string|false|Exact API version filter.| |tags|query|string|false|Comma-separated tag names. Matches APIs tagged with any of the given names.| |view|query|string|false|Developer Portal view name used to filter visible APIs.| @@ -235,6 +216,7 @@ This operation requires Basic Auth authentication. } } ], + "count": 1, "pagination": { "total": 1, "limit": 20, @@ -243,23 +225,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -290,7 +256,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of MCP server metadata DTOs. Always typed `MCP`.|Inline| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)|

Response Schema

@@ -307,7 +273,7 @@ Status Code **200** |---|---|---|---|---| |»» *anonymous*|[ApiInfoResponse](schemas.md#schemaapiinforesponse)|false|none|Fields are returned at the root of ApiMetadataResponse / ApiMetadataCreateResponse (not nested under an `apiInfo` key) — this schema exists only to share the field set between the two via `allOf`.| |»»» name|string|false|none|none| -|»»» apiTitle|string¦null|false|none|none| +|»»» title|string¦null|false|none|none| |»»» remotes|[object]|false|none|none| |»»» version|string|false|none|none| |»»» status|string|false|none|API lifecycle status.| @@ -375,6 +341,7 @@ Status Code **200** |»»» updatedBy|string|false|none|Identity of the user who last updated this API, or `deleted_user` if that user's IDP reference no longer exists. Present on single-resource GET responses only, omitted on list items.| |»»» createdAt|string(date-time)|false|none|none| |»»» updatedAt|string(date-time)|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| @@ -409,7 +376,6 @@ Status Code **200** |Property|Value| |---|---| |status|error| -|status|error| ## Get MCP server metadata @@ -452,7 +418,7 @@ This operation requires Basic Auth authentication. "id": "weather-api-v1", "refId": "cp-api-12345", "name": "Weather API", - "apiTitle": "Weather Forecast API", + "title": "Weather Forecast API", "remotes": [], "version": "v1", "status": "PUBLISHED", @@ -474,23 +440,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -527,7 +477,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|MCP server metadata DTO returned by the service. Always typed `MCP`.|[ApiMetadataResponse](schemas.md#schemaapimetadataresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Plain text success response.|string| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -538,7 +488,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Update MCP server metadata @@ -559,15 +508,14 @@ curl -X PUT https://localhost:3000/api/v0.9/mcp-servers/{mcpServerId} \ ``` -Updates Developer Portal MCP server metadata and, when a `schemaDefinition` is supplied, its stored tools schema. `type` is required and immutable — it must stay `MCP`; any other value is rejected with `400` via the same resolveTypeOrReject check `POST /mcp-servers` uses. An MCP server has no `apiDefinition`; any `apiDefinition` sent on update is ignored. +Updates Developer Portal MCP server metadata and, when a `definition` is supplied, its stored tools schema. `type` is required and immutable — it must stay `MCP`; any other value is rejected with `400` via the same resolveTypeOrReject check `POST /mcp-servers` uses. An MCP server's `definition` is its tools schema, not an OpenAPI-style API contract. > Payload ```yaml -api: string artifact: string -schemaDefinition: string -apiMetadata: '{"name":"Travel Assistant MCP","version":"v1","description":"MCP +definition: string +metadata: '{"name":"Travel Assistant MCP","version":"v1","description":"MCP server for travel planning tools","type":"MCP","agentVisibility":"VISIBLE","status":"PUBLISHED", "tags":["mcp"],"labels":["default"],"endPoints":{"productionURL":"https://mcp.example.com", @@ -586,11 +534,10 @@ This operation requires Basic Auth authentication. |Name|In|Type|Required|Description| |---|---|---|---|---| -|body|body|object|true|MCP server upload. Provide the server metadata via `apiMetadata` (JSON), an `api` YAML file, or a full `artifact` ZIP, together with its `schemaDefinition` — the tools, resources, and prompts the server exposes. An MCP server has no OpenAPI-style `apiDefinition`; its `schemaDefinition` IS its contract, so it is required on create (and replaces the stored schema when supplied on update).| -|» api|body|string(binary)|false|MCP server metadata YAML file (kind MCP).| -|» artifact|body|string(binary)|false|Full MCP server ZIP artifact containing the metadata and the schemaDefinition.| -|» schemaDefinition|body|string(binary)|false|MCP tools schema (YAML or JSON) listing the tools, resources, and prompts the server exposes. Required on create; when supplied on update it replaces the stored schema.| -|» apiMetadata|body|string|false|JSON string accepted when the `api` YAML file is not supplied. Accepted top-level fields: `name`, `version`, `description`, `type` (must be `MCP`), `agentVisibility`, `status`, `referenceId`, `id`, `tags`, `labels`, `owners`, `endPoints` (productionURL, sandboxURL), and `subscriptionPlans` (array of `{ id }` objects — only `id` is read; the plan must already exist in the organization). `id` becomes the MCP server's stored handle.| +|body|body|object|true|MCP server upload. Provide the server metadata via `metadata` (a JSON string or an uploaded YAML/JSON file), or a full `artifact` ZIP, together with its `definition` — the tools, resources, and prompts the server exposes. An MCP server has no OpenAPI-style API contract; its `definition` (tools schema) IS its contract, so it is required on create (and replaces the stored schema when supplied on update).| +|» artifact|body|string(binary)|false|Full MCP server ZIP artifact containing the metadata and the definition.| +|» definition|body|string(binary)|false|MCP tools schema (YAML or JSON) listing the tools, resources, and prompts the server exposes. Required on create; when supplied on update it replaces the stored schema.| +|» metadata|body|string|false|MCP server metadata, supplied either as a JSON string field or as an uploaded YAML/JSON file (a k8s-style document of kind `MCP`; file names `metadata.yaml`/`.yml`/`.json`, or the legacy `api.yaml`/`mcp.yaml`/`devportal.yaml`). As a JSON string it accepts these top-level fields: `name`, `version`, `description`, `type` (must be `MCP`), `agentVisibility`, `status`, `referenceId`, `id`, `tags`, `labels`, `owners`, `endPoints` (productionURL, sandboxURL), and `subscriptionPlans` (array of `{ id }` objects — only `id` is read; the plan must already exist in the organization). `id` becomes the MCP server's stored handle.| |mcpServerId|path|string|true|The MCP server's handle (unique per org).| > Example responses @@ -602,7 +549,7 @@ This operation requires Basic Auth authentication. "id": "weather-api-v1", "refId": "cp-api-12345", "name": "Weather API", - "apiTitle": "Weather Forecast API", + "title": "Weather Forecast API", "remotes": [], "version": "v1", "status": "PUBLISHED", @@ -624,23 +571,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -691,7 +622,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|MCP server metadata DTO returned by the service. Always typed `MCP`.|[ApiMetadataResponse](schemas.md#schemaapimetadataresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -703,7 +634,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Delete MCP server metadata @@ -745,23 +675,7 @@ This operation requires Basic Auth authentication. "string" ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -812,7 +726,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Plain text success response.|string| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -824,4 +738,3 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| diff --git a/docs/rest-apis/devportal/organization-content.md b/docs/rest-apis/devportal/organization-content.md index feee2e98f2..b017e29892 100644 --- a/docs/rest-apis/devportal/organization-content.md +++ b/docs/rest-apis/devportal/organization-content.md @@ -35,13 +35,58 @@ Retrieves a single organization theme asset (CSS, image, etc.) by `fileType` and "string" ``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. + +```json +{ + "status": "error", + "code": "MISSING_REQUIRED_PARAMETER", + "message": "Missing required parameter." +} +``` + +```json +{ + "message": "Missing or invalid fields in the request payload" +} +``` + +> 404 Response + +```json +{ + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." +} +``` + +> 500 Response + +```json +{ + "status": "error", + "code": "INTERNAL_SERVER_ERROR", + "message": "An unexpected error occurred." +} +``` +

Responses

|Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Stored organization content asset.|string| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid or missing `fileType`/`fileName` query parameters.|string| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|No matching organization content asset was found.|string| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +#### Enumerated Values + +|Property|Value| +|---|---| +|status|error| ## Apply a theme @@ -97,23 +142,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -144,7 +173,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Organization content upload accepted and stored successfully.|[OrganizationContentUploadResponse](schemas.md#schemaorganizationcontentuploadresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)|

Response Schema

@@ -154,7 +183,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Reset theme to defaults diff --git a/docs/rest-apis/devportal/organizations.md b/docs/rest-apis/devportal/organizations.md index ce9c755460..f0a5721ffa 100644 --- a/docs/rest-apis/devportal/organizations.md +++ b/docs/rest-apis/devportal/organizations.md @@ -85,23 +85,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -162,7 +146,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Organization created successfully.|[OrganizationResponse](schemas.md#schemaorganizationresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |415|[Unsupported Media Type](https://tools.ietf.org/html/rfc7231#section-6.5.13)|Unsupported request media type.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -175,7 +159,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ### Response Headers @@ -231,6 +214,7 @@ This operation requires Basic Auth authentication. "updatedAt": "2019-08-24T14:15:22Z" } ], + "count": 1, "pagination": { "total": 42, "limit": 20, @@ -274,6 +258,7 @@ Status Code **200** |»»» devportalMode|string|false|none|Controls the mode of the developer portal.| |»» createdAt|string(date-time)¦null|false|none|none| |»» updatedAt|string(date-time)¦null|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| @@ -373,23 +358,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -440,7 +409,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Organization DTO returned by create, update, and lookup operations.|[OrganizationResponse](schemas.md#schemaorganizationresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -452,7 +421,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Get an organization @@ -569,23 +537,7 @@ This operation requires Basic Auth authentication. > Example responses -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -626,7 +578,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Organization deleted successfully.|None| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -637,4 +589,3 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| diff --git a/docs/rest-apis/devportal/providers.md b/docs/rest-apis/devportal/providers.md deleted file mode 100644 index 12f4a66478..0000000000 --- a/docs/rest-apis/devportal/providers.md +++ /dev/null @@ -1,444 +0,0 @@ -

Providers

- -## Create a provider - - - -`POST /o/{orgId}/api/v0.9/provider` - -> Code samples - -```shell - -curl -X POST https://devportal.api-platform.io/o/{orgId}/api/v0.9/provider \ - -u {username}:{password} \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Authorization: Bearer {access-token}' \ - -d @payload.json - -``` - -Creates an API provider configuration for the organization. The current service contract accepts a provider `name` and `providerURL`, rejects unexpected properties, and stores the provider URL as a provider property. - -> Payload - -```json -{ - "name": "string", - "providerURL": "http://example.com" -} -``` - -### Authentication - - - -

Parameters

- -|Name|In|Type|Required|Description| -|---|---|---|---|---| -|body|body|[ProviderRequest](schemas.md#schemaproviderrequest)|true|none| -|orgId|path|string|true|none| - -> Example responses - -> 201 Response - -```json -{ - "orgId": "string", - "name": "string", - "providerURL": "http://example.com" -} -``` - -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "orgName", - "message": "orgName is required." - } - ] - } -] -``` - -```json -{ - "status": "error", - "code": "MISSING_REQUIRED_PARAMETER", - "message": "Missing required parameter." -} -``` - -```json -{ - "message": "Missing or invalid fields in the request payload" -} -``` - -> 409 Response - -```json -{ - "status": "error", - "code": "CONFLICT", - "message": "Conflict" -} -``` - -> 500 Response - -```json -{ - "status": "error", - "code": "INTERNAL_SERVER_ERROR", - "message": "An unexpected error occurred." -} -``` - -

Responses

- -|Status|Meaning|Description|Schema| -|---|---|---|---| -|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Provider DTO returned by create and update operations.|[ProviderResponse](schemas.md#schemaproviderresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| -|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| - -

Response Schema

- -#### Enumerated Values - -|Property|Value| -|---|---| -|status|error| -|status|error| - -## Update a provider - - - -`PUT /o/{orgId}/api/v0.9/provider` - -> Code samples - -```shell - -curl -X PUT https://devportal.api-platform.io/o/{orgId}/api/v0.9/provider \ - -u {username}:{password} \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Authorization: Bearer {access-token}' \ - -d @payload.json - -``` - -Updates the provider URL for an existing organization provider. - -> Payload - -```json -{ - "name": "string", - "providerURL": "http://example.com" -} -``` - -### Authentication - - - -

Parameters

- -|Name|In|Type|Required|Description| -|---|---|---|---|---| -|body|body|[ProviderRequest](schemas.md#schemaproviderrequest)|true|none| -|orgId|path|string|true|none| - -> Example responses - -> 200 Response - -```json -{ - "orgId": "string", - "name": "string", - "providerURL": "http://example.com" -} -``` - -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "orgName", - "message": "orgName is required." - } - ] - } -] -``` - -```json -{ - "status": "error", - "code": "MISSING_REQUIRED_PARAMETER", - "message": "Missing required parameter." -} -``` - -```json -{ - "message": "Missing or invalid fields in the request payload" -} -``` - -> 404 Response - -```json -{ - "status": "error", - "code": "ORG_NOT_FOUND", - "message": "Organization not found." -} -``` - -> 409 Response - -```json -{ - "status": "error", - "code": "CONFLICT", - "message": "Conflict" -} -``` - -> 500 Response - -```json -{ - "status": "error", - "code": "INTERNAL_SERVER_ERROR", - "message": "An unexpected error occurred." -} -``` - -

Responses

- -|Status|Meaning|Description|Schema| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Provider DTO returned by create and update operations.|[ProviderResponse](schemas.md#schemaproviderresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| -|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| - -

Response Schema

- -#### Enumerated Values - -|Property|Value| -|---|---| -|status|error| -|status|error| - -## Get providers - - - -`GET /o/{orgId}/api/v0.9/provider` - -> Code samples - -```shell - -curl -X GET https://devportal.api-platform.io/o/{orgId}/api/v0.9/provider \ - -u {username}:{password} \ - -H 'Accept: application/json' \ - -H 'Authorization: Bearer {access-token}' - -``` - -Returns all providers for the organization. When `name` is supplied, returns the matching provider configuration. - -### Authentication - - - -

Parameters

- -|Name|In|Type|Required|Description| -|---|---|---|---|---| -|name|query|string|false|Optional provider name selector. When omitted, all providers are returned.| -|orgId|path|string|true|none| - -> Example responses - -> 200 Response - -```json -{ - "name": "string", - "providerURL": "http://example.com" -} -``` - -> 404 Response - -```json -{ - "status": "error", - "code": "ORG_NOT_FOUND", - "message": "Organization not found." -} -``` - -> 500 Response - -```json -{ - "status": "error", - "code": "INTERNAL_SERVER_ERROR", - "message": "An unexpected error occurred." -} -``` - -

Responses

- -|Status|Meaning|Description|Schema| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Provider DTO or list of provider DTOs.|Inline| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| -|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| - -

Response Schema

- -## Delete a provider - - - -`DELETE /o/{orgId}/api/v0.9/provider` - -> Code samples - -```shell - -curl -X DELETE https://devportal.api-platform.io/o/{orgId}/api/v0.9/provider?name=string \ - -u {username}:{password} \ - -H 'Accept: application/json' \ - -H 'Authorization: Bearer {access-token}' - -``` - -Deletes an organization provider by `name`. When `property` is supplied, deletes only that provider property. - -### Authentication - - - -

Parameters

- -|Name|In|Type|Required|Description| -|---|---|---|---|---| -|name|query|string|true|Provider name.| -|property|query|string|false|Optional provider property name to delete instead of deleting the whole provider.| -|orgId|path|string|true|none| - -> Example responses - -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "orgName", - "message": "orgName is required." - } - ] - } -] -``` - -```json -{ - "status": "error", - "code": "MISSING_REQUIRED_PARAMETER", - "message": "Missing required parameter." -} -``` - -```json -{ - "message": "Missing or invalid fields in the request payload" -} -``` - -> 404 Response - -```json -{ - "status": "error", - "code": "ORG_NOT_FOUND", - "message": "Organization not found." -} -``` - -> 500 Response - -```json -{ - "status": "error", - "code": "INTERNAL_SERVER_ERROR", - "message": "An unexpected error occurred." -} -``` - -

Responses

- -|Status|Meaning|Description|Schema| -|---|---|---|---| -|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Provider or provider property deleted successfully.|None| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| -|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| - -

Response Schema

- -#### Enumerated Values - -|Property|Value| -|---|---| -|status|error| -|status|error| diff --git a/docs/rest-apis/devportal/schemas.md b/docs/rest-apis/devportal/schemas.md index 9feea9b219..83c8d6a65b 100644 --- a/docs/rest-apis/devportal/schemas.md +++ b/docs/rest-apis/devportal/schemas.md @@ -145,101 +145,6 @@ xor |---|---| |status|error| -

SimpleErrorResponse

- - - - - - -```json -{ - "code": "404", - "message": "Not Found", - "description": "Subscription not found" -} - -``` - -Ad hoc error shape used by the Subscriptions and API Keys handlers, which build error bodies inline instead of going through the shared error formatter. `code` is the HTTP status code as a string, not a catalog code. - -### Properties - -|Name|Type|Required|Restrictions|Description| -|---|---|---|---|---| -|code|string|true|none|none| -|message|string|true|none|none| -|description|string¦null|false|none|none| - -

ValidationErrorList

- - - - - - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "string", - "message": "string" - } - ] - } -] - -``` - -### Properties - -|Name|Type|Required|Restrictions|Description| -|---|---|---|---|---| -|*anonymous*|[[ValidationError](#schemavalidationerror)]|false|none|none| - -

ValidationError

- - - - - - -```json -{ - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "string", - "message": "string" - } - ] -} - -``` - -### Properties - -|Name|Type|Required|Restrictions|Description| -|---|---|---|---|---| -|status|string|true|none|none| -|code|string|true|none|Machine-readable catalog code.| -|message|string|true|none|none| -|errors|[object]|false|none|Per-field validation errors.| -|» field|string|true|none|none| -|» message|string|true|none|none| - -#### Enumerated Values - -|Property|Value| -|---|---| -|status|error| -

OrganizationResponse

@@ -345,7 +250,7 @@ Ad hoc error shape used by the Subscriptions and API Keys handlers, which build ```json { "name": "string", - "apiTitle": "string", + "title": "string", "remotes": [ {} ], @@ -436,7 +341,7 @@ and ```json { "name": "string", - "apiTitle": "string", + "title": "string", "remotes": [ {} ], @@ -539,7 +444,7 @@ and ```json { "name": "string", - "apiTitle": "string", + "title": "string", "remotes": [ {} ], @@ -582,7 +487,7 @@ Fields are returned at the root of ApiMetadataResponse / ApiMetadataCreateRespon |Name|Type|Required|Restrictions|Description| |---|---|---|---|---| |name|string|false|none|none| -|apiTitle|string¦null|false|none|none| +|title|string¦null|false|none|none| |remotes|[object]|false|none|none| |version|string|false|none|none| |status|string|false|none|API lifecycle status.| @@ -1123,7 +1028,7 @@ xor ```json { - "apiId": "weather-api-v1", + "artifactId": "weather-api-v1", "subscriptionPlanId": "Gold" } @@ -1133,7 +1038,7 @@ xor |Name|Type|Required|Restrictions|Description| |---|---|---|---|---| -|apiId|string|true|none|Developer Portal API ID.| +|artifactId|string|true|none|Developer Portal API ID.| |subscriptionPlanId|string|true|none|Developer Portal subscription plan ID.|

SubscriptionUpdateRequest

@@ -1172,7 +1077,7 @@ xor ```json { - "apiId": "weather-api-v1", + "artifactId": "weather-api-v1", "planId": "Gold" } @@ -1182,7 +1087,7 @@ xor |Name|Type|Required|Restrictions|Description| |---|---|---|---|---| -|apiId|string|false|none|Developer Portal API ID the subscription belongs to. Optional — if provided, it is validated against the API derived from the existing subscription record and the request is rejected with 400 if they don't match. It is never used as a fallback: if the API cannot be derived from the subscription record, the request fails with 400 regardless of this value.| +|artifactId|string|false|none|Developer Portal API ID the subscription belongs to. Optional — if provided, it is validated against the API derived from the existing subscription record and the request is rejected with 400 if they don't match. It is never used as a fallback: if the API cannot be derived from the subscription record, the request fails with 400 regardless of this value.| |planId|string|true|none|Developer Portal subscription plan ID to switch to.|

SubscriptionResponse

@@ -1195,7 +1100,7 @@ xor ```json { "subscriptionId": "sub-12345", - "apiId": "weather-api-v1", + "artifactId": "weather-api-v1", "subscriptionToken": "a3f1e8b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1", "subscriptionPlanName": "Gold", "status": "ACTIVE", @@ -1214,7 +1119,7 @@ Subscription payload. |Name|Type|Required|Restrictions|Description| |---|---|---|---|---| |subscriptionId|string|false|none|none| -|apiId|string|false|none|Developer Portal API ID.| +|artifactId|string|false|none|Developer Portal API ID.| |subscriptionToken|string¦null|false|none|Plaintext subscription token, decrypted on every read (not just on create). Null if decryption fails (e.g. the encryption key changed since the token was stored).| |subscriptionPlanName|string|false|none|none| |status|string|false|none|none| diff --git a/docs/rest-apis/devportal/subscription-plans.md b/docs/rest-apis/devportal/subscription-plans.md index 12d536183c..27b23867df 100644 --- a/docs/rest-apis/devportal/subscription-plans.md +++ b/docs/rest-apis/devportal/subscription-plans.md @@ -58,6 +58,7 @@ This operation requires Basic Auth authentication. "updatedAt": "2026-05-07T08:30:00Z" } ], + "count": 1, "pagination": { "total": 42, "limit": 20, @@ -66,23 +67,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -113,7 +98,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of subscription plan DTOs. Empty array when no plans match.|Inline| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)|

Response Schema

@@ -154,6 +139,7 @@ Status Code **200** |»» updatedBy|string|false|none|Identity of the user who last updated this subscription plan, or `deleted_user` if that user's IDP reference no longer exists. Present on single-resource GET responses only, omitted on list items.| |»» createdAt|string(date-time)|false|none|none| |»» updatedAt|string(date-time)|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| @@ -178,7 +164,6 @@ Status Code **200** |Property|Value| |---|---| |status|error| -|status|error| ## Create subscription plans @@ -256,23 +241,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -314,7 +283,7 @@ This operation requires Basic Auth authentication. |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|JSON message response.|[MessageResponse](schemas.md#schemamessageresponse)| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Subscription plan create/update response for single or bulk operations.|Inline| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -339,7 +308,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Upsert subscription plans @@ -433,23 +401,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -501,7 +453,7 @@ This operation requires Basic Auth authentication. |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Subscription plan update response. Bulk updates may return a list, and some configurations return a message.|Inline| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Subscription plan create/update response for single or bulk operations.|Inline| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -541,7 +493,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Get a subscription plan @@ -601,23 +552,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -658,7 +593,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Subscription plan DTO.|[SubscriptionPlanResponse](schemas.md#schemasubscriptionplanresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -669,7 +604,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Delete a subscription plan @@ -705,23 +639,7 @@ This operation requires Basic Auth authentication. > Example responses -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -762,7 +680,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Subscription plan deleted successfully.|None| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -773,4 +691,3 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| diff --git a/docs/rest-apis/devportal/subscriptions.md b/docs/rest-apis/devportal/subscriptions.md index 3293e1b90e..148ae11331 100644 --- a/docs/rest-apis/devportal/subscriptions.md +++ b/docs/rest-apis/devportal/subscriptions.md @@ -25,7 +25,7 @@ Creates a subscription for an API. The API must exist in the Developer Portal an ```json { - "apiId": "weather-api-v1", + "artifactId": "weather-api-v1", "subscriptionPlanId": "Gold" } ``` @@ -41,7 +41,7 @@ This operation requires Basic Auth authentication. |Name|In|Type|Required|Description| |---|---|---|---|---| -|body|body|[SubscriptionCreateRequest](schemas.md#schemasubscriptioncreaterequest)|true|Subscription creation payload. `apiId` is the Developer Portal API ID.| +|body|body|[SubscriptionCreateRequest](schemas.md#schemasubscriptioncreaterequest)|true|Subscription creation payload. `artifactId` is the Developer Portal API ID.| > Example responses @@ -50,7 +50,7 @@ This operation requires Basic Auth authentication. ```json { "subscriptionId": "sub-12345", - "apiId": "weather-api-v1", + "artifactId": "weather-api-v1", "subscriptionToken": "a3f1e8b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1", "subscriptionPlanName": "Gold", "status": "ACTIVE", @@ -59,13 +59,19 @@ This operation requires Basic Auth authentication. } ``` -> 400 Response +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { - "code": "400", - "message": "Bad Request", - "description": "Invalid request parameters" + "status": "error", + "code": "MISSING_REQUIRED_PARAMETER", + "message": "Missing required parameter." +} +``` + +```json +{ + "message": "Missing or invalid fields in the request payload" } ``` @@ -73,9 +79,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -83,9 +89,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "409", - "message": "Conflict", - "description": "A subscription for this API already exists" + "status": "error", + "code": "CONFLICT", + "message": "Conflict" } ``` @@ -104,11 +110,19 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Subscription DTO.|[SubscriptionResponse](schemas.md#schemasubscriptionresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| +

Response Schema

+ +#### Enumerated Values + +|Property|Value| +|---|---| +|status|error| + ### Response Headers |Status|Header|Type|Format|Description| @@ -132,7 +146,7 @@ curl -X GET https://localhost:3000/api/v0.9/subscriptions \ ``` -Lists subscriptions owned by the authenticated user. When `apiId` is provided, results are additionally filtered by the Developer Portal API ID. +Lists subscriptions owned by the authenticated user. When `artifactId` is provided, results are additionally filtered by the Developer Portal API ID. ### Authentication @@ -145,7 +159,7 @@ This operation requires Basic Auth authentication. |Name|In|Type|Required|Description| |---|---|---|---|---| -|apiId|query|string|false|Optional Developer Portal API ID used to filter results.| +|artifactId|query|string|false|Optional Developer Portal API ID used to filter results.| |limit|query|integer|false|Maximum number of records to return.| |offset|query|integer|false|Number of records to skip before returning results.| @@ -158,7 +172,7 @@ This operation requires Basic Auth authentication. "list": [ { "subscriptionId": "sub-12345", - "apiId": "weather-api-v1", + "artifactId": "weather-api-v1", "subscriptionToken": "a3f1e8b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1", "subscriptionPlanName": "Gold", "status": "ACTIVE", @@ -167,6 +181,7 @@ This operation requires Basic Auth authentication. "updatedAt": "2026-05-07T08:30:00Z" } ], + "count": 1, "pagination": { "total": 42, "limit": 20, @@ -179,9 +194,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "API not found" + "status": "error", + "code": "RESOURCE_NOT_FOUND", + "message": "API not found" } ``` @@ -200,7 +215,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of subscription DTOs.|Inline| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Returned when `apiId` is provided but does not match an existing API.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Returned when `artifactId` is provided but does not match an existing API.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)|

Response Schema

@@ -211,7 +226,7 @@ Status Code **200** |---|---|---|---|---| |» list|[[SubscriptionResponse](schemas.md#schemasubscriptionresponse)]|false|none|[Subscription payload.]| |»» subscriptionId|string|false|none|none| -|»» apiId|string|false|none|Developer Portal API ID.| +|»» artifactId|string|false|none|Developer Portal API ID.| |»» subscriptionToken|string¦null|false|none|Plaintext subscription token, decrypted on every read (not just on create). Null if decryption fails (e.g. the encryption key changed since the token was stored).| |»» subscriptionPlanName|string|false|none|none| |»» status|string|false|none|none| @@ -219,6 +234,7 @@ Status Code **200** |»» updatedBy|string|false|none|Identity of the user who last updated the subscription, or `deleted_user` if that user's IDP reference no longer exists. Present on single-resource GET responses only, omitted on list items.| |»» createdAt|string(date-time)|false|none|none| |»» updatedAt|string(date-time)|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| @@ -270,7 +286,7 @@ This operation requires Basic Auth authentication. ```json { "subscriptionId": "sub-12345", - "apiId": "weather-api-v1", + "artifactId": "weather-api-v1", "subscriptionToken": "a3f1e8b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1", "subscriptionPlanName": "Gold", "status": "ACTIVE", @@ -285,9 +301,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -306,7 +322,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Subscription DTO.|[SubscriptionResponse](schemas.md#schemasubscriptionresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| ## Update a subscription @@ -359,7 +375,7 @@ This operation requires Basic Auth authentication. ```json { "subscriptionId": "sub-12345", - "apiId": "weather-api-v1", + "artifactId": "weather-api-v1", "subscriptionToken": "a3f1e8b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1", "subscriptionPlanName": "Gold", "status": "ACTIVE", @@ -370,13 +386,19 @@ This operation requires Basic Auth authentication. } ``` -> 400 Response +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. + +```json +{ + "status": "error", + "code": "MISSING_REQUIRED_PARAMETER", + "message": "Missing required parameter." +} +``` ```json { - "code": "400", - "message": "Bad Request", - "description": "Invalid request parameters" + "message": "Missing or invalid fields in the request payload" } ``` @@ -384,9 +406,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -405,10 +427,18 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Subscription DTO.|[SubscriptionResponse](schemas.md#schemasubscriptionresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| +

Response Schema

+ +#### Enumerated Values + +|Property|Value| +|---|---| +|status|error| + ## Delete a subscription @@ -455,9 +485,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -476,7 +506,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|JSON message response.|[MessageResponse](schemas.md#schemamessageresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| ## Change subscription plan @@ -504,7 +534,7 @@ Changes the subscription plan in-place. The subscription UUID and token remain u ```json { - "apiId": "weather-api-v1", + "artifactId": "weather-api-v1", "planId": "Gold" } ``` @@ -530,7 +560,7 @@ This operation requires Basic Auth authentication. ```json { "subscriptionId": "sub-12345", - "apiId": "weather-api-v1", + "artifactId": "weather-api-v1", "subscriptionToken": "a3f1e8b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1", "subscriptionPlanName": "Gold", "status": "ACTIVE", @@ -541,13 +571,19 @@ This operation requires Basic Auth authentication. } ``` -> 400 Response +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. + +```json +{ + "status": "error", + "code": "MISSING_REQUIRED_PARAMETER", + "message": "Missing required parameter." +} +``` ```json { - "code": "400", - "message": "Bad Request", - "description": "Invalid request parameters" + "message": "Missing or invalid fields in the request payload" } ``` @@ -555,9 +591,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -576,10 +612,18 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Subscription DTO.|[SubscriptionResponse](schemas.md#schemasubscriptionresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| +

Response Schema

+ +#### Enumerated Values + +|Property|Value| +|---|---| +|status|error| + ## Regenerate subscription token @@ -619,7 +663,7 @@ This operation requires Basic Auth authentication. ```json { "subscriptionId": "sub-12345", - "apiId": "weather-api-v1", + "artifactId": "weather-api-v1", "subscriptionToken": "a3f1e8b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1", "subscriptionPlanName": "Gold", "status": "ACTIVE", @@ -634,9 +678,9 @@ This operation requires Basic Auth authentication. ```json { - "code": "404", - "message": "Not Found", - "description": "Subscription not found" + "status": "error", + "code": "ORG_NOT_FOUND", + "message": "Organization not found." } ``` @@ -655,5 +699,5 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Subscription DTO.|[SubscriptionResponse](schemas.md#schemasubscriptionresponse)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[SimpleErrorResponse](schemas.md#schemasimpleerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| diff --git a/docs/rest-apis/devportal/utilities.md b/docs/rest-apis/devportal/utilities.md deleted file mode 100644 index fb35aaacb6..0000000000 --- a/docs/rest-apis/devportal/utilities.md +++ /dev/null @@ -1,113 +0,0 @@ -

Utilities

- -## Create a temporary Arazzo file - - - -`POST /temp-arazzo-file` - -> Code samples - -```shell - -curl -X POST https://devportal.api-platform.io/temp-arazzo-file \ - -u {username}:{password} \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Authorization: Bearer {access-token}' \ - -d @payload.json - -``` - -Writes supplied Arazzo YAML content to a temporary file and returns its path. - -> Payload - -```json -{ - "content": "arazzo: 1.0.1\ninfo:\n title: Weather onboarding\n version: 1.0.0\nworkflows: []\n", - "filename": "workflow.arazzo.yaml" -} -``` - -### Authentication - - - -

Parameters

- -|Name|In|Type|Required|Description| -|---|---|---|---|---| -|body|body|[TempArazzoFileRequest](schemas.md#schematemparazzofilerequest)|true|none| - -> Example responses - -> 200 Response - -```json -{ - "path": "/tmp/arazzo-abc123/workflow.arazzo.yaml" -} -``` - -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` - -```json -{ - "status": "error", - "code": "MISSING_REQUIRED_PARAMETER", - "message": "Missing required parameter." -} -``` - -```json -{ - "message": "Missing or invalid fields in the request payload" -} -``` - -> 500 Response - -```json -{ - "status": "error", - "code": "INTERNAL_SERVER_ERROR", - "message": "An unexpected error occurred." -} -``` - -

Responses

- -|Status|Meaning|Description|Schema| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Path of the temporary Arazzo file.|[TempArazzoFileResponse](schemas.md#schematemparazzofileresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| -|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| - -

Response Schema

- -#### Enumerated Values - -|Property|Value| -|---|---| -|status|error| -|status|error| diff --git a/docs/rest-apis/devportal/views.md b/docs/rest-apis/devportal/views.md index caad76f852..bfaa925c78 100644 --- a/docs/rest-apis/devportal/views.md +++ b/docs/rest-apis/devportal/views.md @@ -57,23 +57,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -114,7 +98,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|JSON message response.|[MessageResponse](schemas.md#schemamessageresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -125,7 +109,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## List views @@ -180,6 +163,7 @@ This operation requires Basic Auth authentication. "updatedAt": "2019-08-24T14:15:22Z" } ], + "count": 1, "pagination": { "total": 42, "limit": 20, @@ -226,6 +210,7 @@ Status Code **200** |»» updatedBy|string|false|none|Identity of the user who last updated this view, or `deleted_user` if that user's IDP reference no longer exists. Present on single-resource GET responses only, omitted on list items.| |»» createdAt|string(date-time)|false|none|none| |»» updatedAt|string(date-time)|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| @@ -292,23 +277,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -359,7 +328,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Echo of the accepted view update payload.|[ViewUpdateRequest](schemas.md#schemaviewupdaterequest)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -371,7 +340,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Get a view @@ -424,23 +392,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -477,7 +429,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|View DTO response.|[ViewResponse](schemas.md#schemaviewresponse)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Plain text success response.|string| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -488,7 +440,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Delete a view @@ -524,23 +475,7 @@ This operation requires Basic Auth authentication. > Example responses -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -581,7 +516,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|View deleted successfully.|None| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -592,4 +527,3 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| diff --git a/docs/rest-apis/devportal/webhook-events.md b/docs/rest-apis/devportal/webhook-events.md index 43de749acd..1bee4dc489 100644 --- a/docs/rest-apis/devportal/webhook-events.md +++ b/docs/rest-apis/devportal/webhook-events.md @@ -72,6 +72,7 @@ This operation requires Basic Auth authentication. ] } ], + "count": 1, "pagination": { "total": 42, "limit": 20, @@ -131,6 +132,7 @@ Status Code **200** |»»» lastError|string¦null|false|none|none| |»»» lastAttemptAt|string(date-time)¦null|false|none|none| |»»» deliveredAt|string(date-time)¦null|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| diff --git a/docs/rest-apis/devportal/webhook-subscribers.md b/docs/rest-apis/devportal/webhook-subscribers.md index a2a838bf58..10de9a7558 100644 --- a/docs/rest-apis/devportal/webhook-subscribers.md +++ b/docs/rest-apis/devportal/webhook-subscribers.md @@ -77,23 +77,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -134,7 +118,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Webhook subscriber configuration response.|[WebhookSubscriberResponseSchema](schemas.md#schemawebhooksubscriberresponseschema)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -145,7 +129,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ### Response Headers @@ -212,6 +195,7 @@ This operation requires Basic Auth authentication. "updatedAt": "2019-08-24T14:15:22Z" } ], + "count": 1, "pagination": { "total": 42, "limit": 20, @@ -257,6 +241,7 @@ Status Code **200** |»» updatedBy|string|false|none|Identity of the user who last updated this webhook subscriber, or `deleted_user` if that user's IDP reference no longer exists. Present on single-resource GET responses only, omitted on list items.| |»» createdAt|string(date-time)|false|none|none| |»» updatedAt|string(date-time)|false|none|none| +|» count|integer|false|none|Number of items returned in this page.| |» pagination|[Pagination](schemas.md#schemapagination)|false|none|Standard pagination metadata returned with collection responses.| |»» total|integer|true|none|Total number of records matching the query.| |»» limit|integer|true|none|Maximum number of records returned in this response.| @@ -425,23 +410,7 @@ This operation requires Basic Auth authentication. } ``` -> Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object. - -```json -[ - { - "status": "error", - "code": "COMMON_VALIDATION_ERROR", - "message": "Input validation failed.", - "errors": [ - { - "field": "name", - "message": "name is required." - } - ] - } -] -``` +> Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object. ```json { @@ -492,7 +461,7 @@ This operation requires Basic Auth authentication. |Status|Meaning|Description|Schema| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Webhook subscriber configuration response.|[WebhookSubscriberResponseSchema](schemas.md#schemawebhooksubscriberresponseschema)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Input validation failures are returned as an array; other bad request errors are returned as a standard error object.|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Validation and other bad-request errors are returned as a standard error object (field-level details, when present, are carried in its `errors` array); some legacy handlers return a message-only object.|Inline| |404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Resource not found.|[ErrorResponse](schemas.md#schemaerrorresponse)| |409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|The request conflicts with an existing resource.|[ErrorResponse](schemas.md#schemaerrorresponse)| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error.|[ErrorResponse](schemas.md#schemaerrorresponse)| @@ -504,7 +473,6 @@ This operation requires Basic Auth authentication. |Property|Value| |---|---| |status|error| -|status|error| ## Delete a webhook subscriber diff --git a/portals/developer-portal/docs/devportal-openapi-spec-v0.9.yaml b/portals/developer-portal/docs/devportal-openapi-spec-v0.9.yaml index 1d44b7d64b..46f343b3cc 100644 --- a/portals/developer-portal/docs/devportal-openapi-spec-v0.9.yaml +++ b/portals/developer-portal/docs/devportal-openapi-spec-v0.9.yaml @@ -188,17 +188,11 @@ paths: "200": $ref: "#/components/responses/OrganizationContentAssetResponse" "400": - description: Invalid or missing `fileType`/`fileName` query parameters. - content: - text/plain: - schema: - type: string + $ref: "#/components/responses/BadRequest" "404": - description: No matching organization content asset was found. - content: - text/plain: - schema: - type: string + $ref: "#/components/responses/NotFound" + "500": + $ref: "#/components/responses/InternalServerError" security: [] /views/{viewId}/apply-theme: parameters: @@ -277,17 +271,17 @@ paths: summary: Create API metadata description: >- Creates Developer Portal API metadata from either a full API artifact ZIP, an API metadata YAML file (`api.yaml` - / `devportal.yaml` / `mcp.yaml`), or an `apiMetadata` JSON string. An API definition file is required unless + / `devportal.yaml` / `mcp.yaml`), or a `metadata` JSON string. An API definition file is required unless supplied by the artifact ZIP. The YAML `spec` block accepts: `displayName`, `version`, `description`, `type`, `status`, `agentVisibility`, `tags`, `labels`, `referenceId`, `endpoints` (sandboxUrl, productionUrl), `businessInformation` (owners), and `subscriptionPlans`. The service also stores labels, subscription plan mappings, image metadata, and schema definitions for - GraphQL APIs when provided. Via the JSON `apiMetadata` field, `type` is required — an omitted type is + GraphQL APIs when provided. Via the JSON `metadata` field, `type` is required — an omitted type is rejected with `400` (via YAML, an omitted `spec.type` defaults to `REST`). MCP servers must be created via `POST /api/v0.9/mcp-servers` instead — a request whose resolved `type` is `MCP` is rejected with `400`. `subscriptionPlans` links existing org-level plans to this API by name — it does not create plans. In YAML it is - a string array (`["Gold", "Silver"]`). In the JSON `apiMetadata` field it is an object array where only + a string array (`["Gold", "Silver"]`). In the JSON `metadata` field it is an object array where only `id` is used (`[{"id":"Gold"}]`); extra fields such as `planId`, `displayName`, or `requestCount` are ignored. operationId: createApiMetadata @@ -318,11 +312,11 @@ paths: are never returned here — use `GET /api/v0.9/mcp-servers`. operationId: getAllApiMetadataForOrganization parameters: - - $ref: "#/components/parameters/apiSearchQuery" - - $ref: "#/components/parameters/apiNameQuery" - - $ref: "#/components/parameters/apiVersionQuery" - - $ref: "#/components/parameters/apiTagsQuery" - - $ref: "#/components/parameters/apiViewQuery" + - $ref: "#/components/parameters/searchQuery" + - $ref: "#/components/parameters/nameQuery" + - $ref: "#/components/parameters/versionQuery" + - $ref: "#/components/parameters/tagsQuery" + - $ref: "#/components/parameters/viewQuery" - $ref: "#/components/parameters/limit" - $ref: "#/components/parameters/offset" responses: @@ -364,7 +358,7 @@ paths: summary: Update API metadata description: >- Updates Developer Portal API metadata and its stored definition. Accepts the same YAML spec fields and - `apiMetadata` JSON format as the create operation. The update flow can also adjust label mappings, subscription + `metadata` JSON format as the create operation. The update flow can also adjust label mappings, subscription plan mappings, schema definitions, and image metadata. Status changes to unpublished are rejected when active subscriptions exist. `type` is required (see the create operation) and is immutable — it must match the API's existing type; a different value is rejected with `409`. @@ -539,7 +533,7 @@ paths: description: | Uploads the static content package for an API. - The `apiContent` ZIP must contain at least one of these root directories: + The `content` ZIP must contain at least one of these root directories: - `web/` for API landing-page assets such as markdown, HTML, CSS, JavaScript, and images. - `docs/` for downloadable API documents. @@ -547,7 +541,7 @@ paths: Use `imageMetadata` to map uploaded images to API image roles such as the API icon. operationId: createApiContent requestBody: - $ref: "#/components/requestBodies/ApiContentZipMultipartBody" + $ref: "#/components/requestBodies/ContentZipMultipartBody" responses: "201": $ref: "#/components/responses/MessageResponse" @@ -573,7 +567,7 @@ paths: Image metadata is updated only when image metadata can be resolved from the upload or request body. operationId: replaceApiContent requestBody: - $ref: "#/components/requestBodies/ApiContentZipMultipartBody" + $ref: "#/components/requestBodies/ContentZipMultipartBody" responses: "201": $ref: "#/components/responses/MessageResponse" @@ -603,11 +597,11 @@ paths: - dp:api_content_read - dp:api_content_manage parameters: - - $ref: "#/components/parameters/apiContentTypeQuery" - - $ref: "#/components/parameters/apiContentFileNameQuery" + - $ref: "#/components/parameters/contentTypeQuery" + - $ref: "#/components/parameters/contentFileNameQuery" responses: "200": - $ref: "#/components/responses/APIContentAssetResponse" + $ref: "#/components/responses/ContentAssetResponse" "400": $ref: "#/components/responses/BadRequest" "404": @@ -625,7 +619,7 @@ paths: files matching that content category for the API. operationId: deleteApiContentFile parameters: - - $ref: "#/components/parameters/apiContentTypeQuery" + - $ref: "#/components/parameters/contentTypeQuery" - $ref: "#/components/parameters/fileNameQuery" responses: "204": @@ -905,18 +899,18 @@ paths: $ref: "#/components/schemas/SubscriptionResponse" example: subscriptionId: sub-12345 - apiId: weather-api-v1 + artifactId: weather-api-v1 subscriptionToken: a3f1e8b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1 subscriptionPlanName: Gold status: ACTIVE createdBy: alice@example.com createdAt: "2026-05-07T08:30:00Z" "400": - $ref: "#/components/responses/SimpleBadRequest" + $ref: "#/components/responses/BadRequest" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "409": - $ref: "#/components/responses/SimpleConflict" + $ref: "#/components/responses/Conflict" "500": $ref: "#/components/responses/InternalServerError" security: @@ -929,25 +923,25 @@ paths: operationId: listSubscriptions summary: List subscriptions description: >- - Lists subscriptions owned by the authenticated user. When `apiId` is provided, results are additionally filtered + Lists subscriptions owned by the authenticated user. When `artifactId` is provided, results are additionally filtered by the Developer Portal API ID. parameters: - - $ref: "#/components/parameters/apiIdQueryOptional" + - $ref: "#/components/parameters/artifactIdQueryOptional" - $ref: "#/components/parameters/limit" - $ref: "#/components/parameters/offset" responses: "200": $ref: "#/components/responses/SubscriptionListResponse" "404": - description: Returned when `apiId` is provided but does not match an existing API. + description: Returned when `artifactId` is provided but does not match an existing API. content: application/json: schema: - $ref: "#/components/schemas/SimpleErrorResponse" + $ref: "#/components/schemas/ErrorResponse" example: - code: "404" - message: Not Found - description: API not found + status: error + code: RESOURCE_NOT_FOUND + message: API not found "500": $ref: "#/components/responses/InternalServerError" security: @@ -967,7 +961,7 @@ paths: "200": $ref: "#/components/responses/SubscriptionResponse" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "500": $ref: "#/components/responses/InternalServerError" security: @@ -986,9 +980,9 @@ paths: "200": $ref: "#/components/responses/SubscriptionResponse" "400": - $ref: "#/components/responses/SimpleBadRequest" + $ref: "#/components/responses/BadRequest" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "500": $ref: "#/components/responses/InternalServerError" security: @@ -1005,7 +999,7 @@ paths: "200": $ref: "#/components/responses/MessageResponse" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "500": $ref: "#/components/responses/InternalServerError" security: @@ -1030,9 +1024,9 @@ paths: "200": $ref: "#/components/responses/SubscriptionResponse" "400": - $ref: "#/components/responses/SimpleBadRequest" + $ref: "#/components/responses/BadRequest" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "500": $ref: "#/components/responses/InternalServerError" security: @@ -1056,7 +1050,7 @@ paths: "200": $ref: "#/components/responses/SubscriptionResponse" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "500": $ref: "#/components/responses/InternalServerError" security: @@ -1099,11 +1093,11 @@ paths: expiresAt: "2026-12-31T23:59:59Z" status: ACTIVE "400": - $ref: "#/components/responses/SimpleBadRequest" + $ref: "#/components/responses/BadRequest" "403": - $ref: "#/components/responses/SimpleForbidden" + $ref: "#/components/responses/Forbidden" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "500": $ref: "#/components/responses/InternalServerError" security: @@ -1152,17 +1146,18 @@ paths: "200": $ref: "#/components/responses/ApiKeyGenerateResponse" "403": - $ref: "#/components/responses/SimpleForbidden" + $ref: "#/components/responses/Forbidden" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "409": description: The key has already been revoked and cannot be regenerated. content: application/json: schema: - $ref: "#/components/schemas/SimpleErrorResponse" + $ref: "#/components/schemas/ErrorResponse" example: - code: "409" + status: error + code: CONFLICT message: Cannot regenerate a revoked key "500": $ref: "#/components/responses/InternalServerError" @@ -1188,17 +1183,18 @@ paths: "204": description: API key revoked successfully. "403": - $ref: "#/components/responses/SimpleForbidden" + $ref: "#/components/responses/Forbidden" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "409": description: The key has already been revoked. content: application/json: schema: - $ref: "#/components/schemas/SimpleErrorResponse" + $ref: "#/components/schemas/ErrorResponse" example: - code: "409" + status: error + code: CONFLICT message: Key already revoked or not found "500": $ref: "#/components/responses/InternalServerError" @@ -1228,19 +1224,20 @@ paths: schema: $ref: "#/components/schemas/ApiKeyApplicationResponse" "400": - $ref: "#/components/responses/SimpleBadRequest" + $ref: "#/components/responses/BadRequest" "403": - $ref: "#/components/responses/SimpleForbidden" + $ref: "#/components/responses/Forbidden" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "409": description: The key has already been revoked and cannot be associated with an application. content: application/json: schema: - $ref: "#/components/schemas/SimpleErrorResponse" + $ref: "#/components/schemas/ErrorResponse" example: - code: "409" + status: error + code: CONFLICT message: Cannot associate a revoked key "500": $ref: "#/components/responses/InternalServerError" @@ -1265,9 +1262,9 @@ paths: "204": description: Association removed (or none existed). "403": - $ref: "#/components/responses/SimpleForbidden" + $ref: "#/components/responses/Forbidden" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "500": $ref: "#/components/responses/InternalServerError" security: @@ -1281,10 +1278,10 @@ paths: summary: Create MCP server metadata description: >- Creates Developer Portal MCP server metadata. Accepts the same metadata input formats as - `POST /api/v0.9/apis` (artifact ZIP, `api.yaml` / `devportal.yaml` / `mcp.yaml`, or `apiMetadata` JSON), - but the created record is always typed `MCP`. An MCP server's contract is its `schemaDefinition` — the - tools, resources, and prompts it exposes — not an OpenAPI-style `apiDefinition`; a `schemaDefinition` is - required and any `apiDefinition` sent is ignored. Via the JSON `apiMetadata` field, `type` must be + `POST /api/v0.9/apis` (artifact ZIP, `api.yaml` / `devportal.yaml` / `mcp.yaml`, or `metadata` JSON), + but the created record is always typed `MCP`. An MCP server's contract is its `definition` (tools schema) — the + tools, resources, and prompts it exposes — not an OpenAPI-style API contract; a `definition` is + required. Via the JSON `metadata` field, `type` must be explicitly `MCP`; an omitted type or any other value is rejected with a 400 (use `POST /api/v0.9/apis`). operationId: createMcpServer requestBody: @@ -1313,11 +1310,11 @@ paths: records. operationId: getAllMcpServersForOrganization parameters: - - $ref: "#/components/parameters/apiSearchQuery" - - $ref: "#/components/parameters/apiNameQuery" - - $ref: "#/components/parameters/apiVersionQuery" - - $ref: "#/components/parameters/apiTagsQuery" - - $ref: "#/components/parameters/apiViewQuery" + - $ref: "#/components/parameters/searchQuery" + - $ref: "#/components/parameters/nameQuery" + - $ref: "#/components/parameters/versionQuery" + - $ref: "#/components/parameters/tagsQuery" + - $ref: "#/components/parameters/viewQuery" - $ref: "#/components/parameters/limit" - $ref: "#/components/parameters/offset" responses: @@ -1358,10 +1355,10 @@ paths: - MCP Servers summary: Update MCP server metadata description: >- - Updates Developer Portal MCP server metadata and, when a `schemaDefinition` is supplied, its stored + Updates Developer Portal MCP server metadata and, when a `definition` is supplied, its stored tools schema. `type` is required and immutable — it must stay `MCP`; any other value is rejected with - `400` via the same resolveTypeOrReject check `POST /mcp-servers` uses. An MCP server has no - `apiDefinition`; any `apiDefinition` sent on update is ignored. + `400` via the same resolveTypeOrReject check `POST /mcp-servers` uses. An MCP server's `definition` + is its tools schema, not an OpenAPI-style API contract. operationId: updateMcpServer requestBody: $ref: "#/components/requestBodies/McpServerMultipartBody" @@ -1412,7 +1409,7 @@ paths: Uploads the static content package for an MCP server. Mirrors `POST /api/v0.9/apis/{apiId}/assets`. operationId: createMcpServerContent requestBody: - $ref: "#/components/requestBodies/ApiContentZipMultipartBody" + $ref: "#/components/requestBodies/ContentZipMultipartBody" responses: "201": $ref: "#/components/responses/MessageResponse" @@ -1434,7 +1431,7 @@ paths: Replaces or adds static content files for an existing MCP server. Mirrors `PUT /api/v0.9/apis/{apiId}/assets`. operationId: replaceMcpServerContent requestBody: - $ref: "#/components/requestBodies/ApiContentZipMultipartBody" + $ref: "#/components/requestBodies/ContentZipMultipartBody" responses: "201": $ref: "#/components/responses/MessageResponse" @@ -1459,11 +1456,11 @@ paths: - dp:mcp_content_read - dp:mcp_content_manage parameters: - - $ref: "#/components/parameters/apiContentTypeQuery" - - $ref: "#/components/parameters/apiContentFileNameQuery" + - $ref: "#/components/parameters/contentTypeQuery" + - $ref: "#/components/parameters/contentFileNameQuery" responses: "200": - $ref: "#/components/responses/APIContentAssetResponse" + $ref: "#/components/responses/ContentAssetResponse" "400": $ref: "#/components/responses/BadRequest" "404": @@ -1478,7 +1475,7 @@ paths: Deletes stored MCP server content. Mirrors `DELETE /api/v0.9/apis/{apiId}/assets`. operationId: deleteMcpServerContentFile parameters: - - $ref: "#/components/parameters/apiContentTypeQuery" + - $ref: "#/components/parameters/contentTypeQuery" - $ref: "#/components/parameters/fileNameQuery" responses: "204": @@ -1519,11 +1516,11 @@ paths: schema: $ref: "#/components/schemas/ApiKeyResponse" "400": - $ref: "#/components/responses/SimpleBadRequest" + $ref: "#/components/responses/BadRequest" "403": - $ref: "#/components/responses/SimpleForbidden" + $ref: "#/components/responses/Forbidden" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "500": $ref: "#/components/responses/InternalServerError" security: @@ -1571,17 +1568,18 @@ paths: "200": $ref: "#/components/responses/ApiKeyGenerateResponse" "403": - $ref: "#/components/responses/SimpleForbidden" + $ref: "#/components/responses/Forbidden" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "409": description: The key has already been revoked and cannot be regenerated. content: application/json: schema: - $ref: "#/components/schemas/SimpleErrorResponse" + $ref: "#/components/schemas/ErrorResponse" example: - code: "409" + status: error + code: CONFLICT message: Cannot regenerate a revoked key "500": $ref: "#/components/responses/InternalServerError" @@ -1606,17 +1604,18 @@ paths: "204": description: API key revoked successfully. "403": - $ref: "#/components/responses/SimpleForbidden" + $ref: "#/components/responses/Forbidden" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "409": description: The key has already been revoked. content: application/json: schema: - $ref: "#/components/schemas/SimpleErrorResponse" + $ref: "#/components/schemas/ErrorResponse" example: - code: "409" + status: error + code: CONFLICT message: Key already revoked or not found "500": $ref: "#/components/responses/InternalServerError" @@ -1645,19 +1644,20 @@ paths: schema: $ref: "#/components/schemas/ApiKeyApplicationResponse" "400": - $ref: "#/components/responses/SimpleBadRequest" + $ref: "#/components/responses/BadRequest" "403": - $ref: "#/components/responses/SimpleForbidden" + $ref: "#/components/responses/Forbidden" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "409": description: The key has already been revoked and cannot be associated with an application. content: application/json: schema: - $ref: "#/components/schemas/SimpleErrorResponse" + $ref: "#/components/schemas/ErrorResponse" example: - code: "409" + status: error + code: CONFLICT message: Cannot associate a revoked key "500": $ref: "#/components/responses/InternalServerError" @@ -1682,9 +1682,9 @@ paths: "204": description: Association removed (or none existed). "403": - $ref: "#/components/responses/SimpleForbidden" + $ref: "#/components/responses/Forbidden" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "500": $ref: "#/components/responses/InternalServerError" security: @@ -1709,7 +1709,7 @@ paths: "200": $ref: "#/components/responses/ApiKeyListResponse" "404": - $ref: "#/components/responses/SimpleNotFound" + $ref: "#/components/responses/NotFound" "500": $ref: "#/components/responses/InternalServerError" security: @@ -2552,7 +2552,7 @@ components: type: string example: workflow-12345 description: The API workflow's handle (unique per org and view). - apiContentTypeQuery: + contentTypeQuery: name: type in: query required: true @@ -2562,7 +2562,7 @@ components: description: >- Stored API content type selector. Common values are `web`, `document`, `image`, and `link`, depending on how the uploaded ZIP content was classified. - apiContentFileNameQuery: + contentFileNameQuery: name: fileName in: query required: true @@ -2599,7 +2599,7 @@ components: schema: type: string description: Optional relative content path used together with `fileType` and `fileName`. - apiSearchQuery: + searchQuery: name: query in: query required: false @@ -2607,15 +2607,15 @@ components: type: string example: weather description: Free-text API metadata search term. - apiNameQuery: - name: apiName + nameQuery: + name: name in: query required: false schema: type: string example: Weather API description: Exact API name filter. - apiVersionQuery: + versionQuery: name: version in: query required: false @@ -2623,7 +2623,7 @@ components: type: string example: v1 description: Exact API version filter. - apiTagsQuery: + tagsQuery: name: tags in: query required: false @@ -2631,7 +2631,7 @@ components: type: string example: weather,public description: Comma-separated tag names. Matches APIs tagged with any of the given names. - apiViewQuery: + viewQuery: name: view in: query required: false @@ -2639,16 +2639,8 @@ components: type: string example: default description: Developer Portal view name used to filter visible APIs. - apiIdQueryRequired: - name: apiId - in: query - required: true - schema: - type: string - example: weather-api-v1 - description: Developer Portal API ID. - apiIdQueryOptional: - name: apiId + artifactIdQueryOptional: + name: artifactId in: query required: false schema: @@ -2805,13 +2797,13 @@ components: displayName: Premium APIs SubscriptionCreateBody: required: true - description: Subscription creation payload. `apiId` is the Developer Portal API ID. + description: Subscription creation payload. `artifactId` is the Developer Portal API ID. content: application/json: schema: $ref: "#/components/schemas/SubscriptionCreateRequest" example: - apiId: weather-api-v1 + artifactId: weather-api-v1 subscriptionPlanId: Gold SubscriptionUpdateBody: required: true @@ -3145,34 +3137,31 @@ components: ApiMetadataMultipartBody: required: true description: >- - API metadata upload. Send either `artifact`, or `api` with `apiDefinition`, or `apiMetadata` with - `apiDefinition`. `schemaDefinition` carries a GraphQL SDL schema. (MCP servers are created via - `/mcp-servers` with the dedicated `McpServerMultipartBody`, not this body.) + API metadata upload. Send either `artifact`, or `metadata` with `definition`. For a GraphQL API + the `definition` field carries the SDL schema. (MCP servers are created via `/mcp-servers` with + the dedicated `McpServerMultipartBody`, not this body.) content: multipart/form-data: schema: type: object properties: - api: + definition: type: string format: binary - description: API metadata YAML file. - apiDefinition: - type: string - format: binary - description: API definition file. + description: >- + API definition file. For REST/SOAP/etc. this is the OpenAPI/WSDL/AsyncAPI contract; + for a GraphQL API it is the SDL schema. artifact: type: string format: binary description: Full API ZIP artifact containing metadata and definition files. - schemaDefinition: - type: string - format: binary - description: GraphQL SDL schema definition file. - apiMetadata: + metadata: type: string description: >- - JSON string accepted by the service when the `api` YAML file is not supplied. Accepted top-level + API metadata, supplied either as a JSON string field or as an uploaded YAML/JSON file + (a k8s-style document with `kind`, `metadata.name`, and a `spec` block; file names + `metadata.yaml`/`.yml`/`.json`, or the legacy `api.yaml`/`mcp.yaml`/`devportal.yaml`). + As a JSON string it accepts these top-level fields: `name`, `version`, `description`, `type`, `agentVisibility`, `status`, `referenceId`, `id`, `tags`, `labels`, `owners`, `endPoints` (productionURL, sandboxURL), and `subscriptionPlans` (array of `{ @@ -3188,34 +3177,33 @@ components: McpServerMultipartBody: required: true description: >- - MCP server upload. Provide the server metadata via `apiMetadata` (JSON), an `api` YAML file, - or a full `artifact` ZIP, together with its `schemaDefinition` — the tools, resources, and - prompts the server exposes. An MCP server has no OpenAPI-style `apiDefinition`; its - `schemaDefinition` IS its contract, so it is required on create (and replaces the stored - schema when supplied on update). + MCP server upload. Provide the server metadata via `metadata` (a JSON string or an uploaded + YAML/JSON file), or a full `artifact` ZIP, together with its `definition` — the tools, resources, + and prompts the server exposes. An MCP server has no OpenAPI-style API contract; its + `definition` (tools schema) IS its contract, so it is required on create (and replaces the + stored schema when supplied on update). content: multipart/form-data: schema: type: object properties: - api: - type: string - format: binary - description: MCP server metadata YAML file (kind MCP). artifact: type: string format: binary - description: Full MCP server ZIP artifact containing the metadata and the schemaDefinition. - schemaDefinition: + description: Full MCP server ZIP artifact containing the metadata and the definition. + definition: type: string format: binary description: >- MCP tools schema (YAML or JSON) listing the tools, resources, and prompts the server exposes. Required on create; when supplied on update it replaces the stored schema. - apiMetadata: + metadata: type: string description: >- - JSON string accepted when the `api` YAML file is not supplied. Accepted top-level fields: + MCP server metadata, supplied either as a JSON string field or as an uploaded YAML/JSON + file (a k8s-style document of kind `MCP`; file names `metadata.yaml`/`.yml`/`.json`, or + the legacy `api.yaml`/`mcp.yaml`/`devportal.yaml`). As a JSON string it accepts these + top-level fields: `name`, `version`, `description`, `type` (must be `MCP`), `agentVisibility`, `status`, `referenceId`, `id`, `tags`, `labels`, `owners`, `endPoints` (productionURL, sandboxURL), and `subscriptionPlans` (array of `{ id }` objects — only `id` is read; the plan must already @@ -3225,7 +3213,7 @@ components: planning tools","type":"MCP","agentVisibility":"VISIBLE","status":"PUBLISHED", "tags":["mcp"],"labels":["default"],"endPoints":{"productionURL":"https://mcp.example.com", "sandboxURL":"https://mcp.example.com"},"subscriptionPlans":[{"id":"Gold"}]} - ApiContentZipMultipartBody: + ContentZipMultipartBody: required: true description: | API content ZIP upload. @@ -3241,12 +3229,12 @@ components: schema: type: object required: - - apiContent + - content properties: - apiContent: + content: type: string format: binary - description: ZIP upload field named `apiContent`. + description: ZIP upload field named `content`. docMetadata: type: string description: Optional JSON string containing API document link metadata. @@ -3324,6 +3312,10 @@ components: type: array items: $ref: "#/components/schemas/OrganizationResponse" + count: + type: integer + description: Number of items returned in this page. + example: 1 pagination: $ref: "#/components/schemas/Pagination" OrganizationContentUploadResponse: @@ -3366,7 +3358,7 @@ components: schema: type: string format: binary - APIContentAssetResponse: + ContentAssetResponse: description: >- Stored API content asset. The concrete media type depends on the stored file extension or whether the content is an external document link. @@ -3421,25 +3413,16 @@ components: $ref: "#/components/schemas/OrganizationContentListItemResponse" BadRequest: description: >- - Bad request. Input validation failures are returned as an array; other bad request errors are returned as a - standard error object. + Bad request. Validation and other bad-request errors are returned as a standard error object + (field-level details, when present, are carried in its `errors` array); some legacy handlers + return a message-only object. content: application/json: schema: oneOf: - - $ref: "#/components/schemas/ValidationErrorList" - $ref: "#/components/schemas/ErrorResponse" - $ref: "#/components/schemas/MessageResponse" examples: - validationError: - summary: Validation failure - value: - - status: error - code: COMMON_VALIDATION_ERROR - message: Input validation failed. - errors: - - field: name - message: name is required. badRequest: summary: Bad request error value: @@ -3470,45 +3453,6 @@ components: status: error code: CONFLICT message: Conflict - SimpleBadRequest: - description: Bad request. - content: - application/json: - schema: - $ref: "#/components/schemas/SimpleErrorResponse" - example: - code: "400" - message: Bad Request - description: Invalid request parameters - SimpleNotFound: - description: Resource not found. - content: - application/json: - schema: - $ref: "#/components/schemas/SimpleErrorResponse" - example: - code: "404" - message: Not Found - description: Subscription not found - SimpleConflict: - description: The request conflicts with an existing resource. - content: - application/json: - schema: - $ref: "#/components/schemas/SimpleErrorResponse" - example: - code: "409" - message: Conflict - description: A subscription for this API already exists - SimpleForbidden: - description: Request is forbidden for the current runtime mode (e.g. read-only mode). - content: - application/json: - schema: - $ref: "#/components/schemas/SimpleErrorResponse" - example: - code: "403" - message: Read-only mode UnsupportedMediaType: description: Unsupported request media type. content: @@ -3567,7 +3511,7 @@ components: id: weather-api-v1 refId: cp-api-12345 name: Weather API - apiTitle: Weather Forecast API + title: Weather Forecast API version: v1 status: PUBLISHED description: Weather forecast API. @@ -3592,7 +3536,7 @@ components: id: weather-api-v1 refId: cp-api-12345 name: Weather API - apiTitle: Weather Forecast API + title: Weather Forecast API remotes: [] version: v1 status: PUBLISHED @@ -3621,6 +3565,10 @@ components: type: array items: $ref: "#/components/schemas/ApiMetadataResponse" + count: + type: integer + description: Number of items returned in this page. + example: 1 pagination: $ref: "#/components/schemas/Pagination" example: @@ -3638,6 +3586,7 @@ components: endPoints: sandboxURL: https://sandbox.example.com/weather productionURL: https://api.example.com/weather + count: 1 pagination: total: 1 limit: 20 @@ -3658,7 +3607,7 @@ components: id: weather-api-v1 refId: cp-api-12345 name: Weather API - apiTitle: Weather Forecast API + title: Weather Forecast API version: v1 status: PUBLISHED description: Weather forecast API. @@ -3683,7 +3632,7 @@ components: id: weather-api-v1 refId: cp-api-12345 name: Weather API - apiTitle: Weather Forecast API + title: Weather Forecast API remotes: [] version: v1 status: PUBLISHED @@ -3708,6 +3657,10 @@ components: type: array items: $ref: "#/components/schemas/ApiMetadataResponse" + count: + type: integer + description: Number of items returned in this page. + example: 1 pagination: $ref: "#/components/schemas/Pagination" example: @@ -3725,6 +3678,7 @@ components: endPoints: sandboxURL: https://sandbox.example.com/weather productionURL: https://api.example.com/weather + count: 1 pagination: total: 1 limit: 20 @@ -3746,6 +3700,10 @@ components: type: array items: $ref: "#/components/schemas/SubscriptionPlanResponse" + count: + type: integer + description: Number of items returned in this page. + example: 1 pagination: $ref: "#/components/schemas/Pagination" example: @@ -3763,6 +3721,7 @@ components: createdBy: alice@example.com createdAt: "2026-05-07T08:30:00Z" updatedAt: "2026-05-07T08:30:00Z" + count: 1 pagination: total: 42 limit: 20 @@ -3799,6 +3758,10 @@ components: type: array items: $ref: "#/components/schemas/LabelResponse" + count: + type: integer + description: Number of items returned in this page. + example: 1 pagination: $ref: "#/components/schemas/Pagination" LabelResponse: @@ -3833,6 +3796,10 @@ components: type: array items: $ref: "#/components/schemas/ApplicationResponse" + count: + type: integer + description: Number of items returned in this page. + example: 1 pagination: $ref: "#/components/schemas/Pagination" example: @@ -3844,6 +3811,7 @@ components: - asClientId: asgardeo-client-abc123 kmId: km-uuid-12345 type: PRODUCTION + count: 1 pagination: total: 1 limit: 20 @@ -3856,7 +3824,7 @@ components: $ref: "#/components/schemas/SubscriptionResponse" example: subscriptionId: sub-12345 - apiId: weather-api-v1 + artifactId: weather-api-v1 subscriptionToken: a3f1e8b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1 subscriptionPlanName: Gold status: ACTIVE @@ -3875,18 +3843,23 @@ components: type: array items: $ref: "#/components/schemas/SubscriptionResponse" + count: + type: integer + description: Number of items returned in this page. + example: 1 pagination: $ref: "#/components/schemas/Pagination" example: list: - subscriptionId: sub-12345 - apiId: weather-api-v1 + artifactId: weather-api-v1 subscriptionToken: a3f1e8b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1b3c5d7e9f10b2c4d6e8f0a1 subscriptionPlanName: Gold status: ACTIVE createdBy: alice@example.com createdAt: "2026-05-07T08:30:00Z" updatedAt: "2026-05-07T08:30:00Z" + count: 1 pagination: total: 42 limit: 20 @@ -3915,6 +3888,10 @@ components: type: array items: $ref: "#/components/schemas/ApiKeyMetadataResponse" + count: + type: integer + description: Number of items returned in this page. + example: 1 pagination: $ref: "#/components/schemas/Pagination" KeyManagerResponse: @@ -3938,6 +3915,10 @@ components: anyOf: - $ref: "#/components/schemas/KeyManagerResponseSchema" - $ref: "#/components/schemas/KeyManagerPublicResponseSchema" + count: + type: integer + description: Number of items returned in this page. + example: 1 pagination: $ref: "#/components/schemas/Pagination" example: @@ -3951,6 +3932,7 @@ components: createdBy: alice@example.com createdAt: "2026-05-07T08:30:00Z" updatedAt: "2026-05-07T08:30:00Z" + count: 1 pagination: total: 42 limit: 20 @@ -3972,6 +3954,10 @@ components: type: array items: $ref: "#/components/schemas/WebhookSubscriberResponseSchema" + count: + type: integer + description: Number of items returned in this page. + example: 1 pagination: $ref: "#/components/schemas/Pagination" ApplicationKeyResponse: @@ -4046,6 +4032,10 @@ components: type: array items: $ref: "#/components/schemas/APIWorkflowResponse" + count: + type: integer + description: Number of items returned in this page. + example: 1 pagination: $ref: "#/components/schemas/Pagination" APIWorkflowPromptResponse: @@ -4083,6 +4073,10 @@ components: type: array items: $ref: "#/components/schemas/ViewResponse" + count: + type: integer + description: Number of items returned in this page. + example: 1 pagination: $ref: "#/components/schemas/Pagination" ViewUpdateEchoResponse: @@ -4107,6 +4101,10 @@ components: type: array items: $ref: "#/components/schemas/WebhookEvent" + count: + type: integer + description: Number of items returned in this page. + example: 1 pagination: $ref: "#/components/schemas/Pagination" WebhookEventResponse: @@ -4192,64 +4190,6 @@ components: type: string additionalProperties: false additionalProperties: false - SimpleErrorResponse: - type: object - description: >- - Ad hoc error shape used by the Subscriptions and API Keys handlers, which build error bodies inline instead of - going through the shared error formatter. `code` is the HTTP status code as a string, not a catalog code. - required: - - code - - message - properties: - code: - type: string - example: "404" - message: - type: string - example: Not Found - description: - type: string - nullable: true - example: Subscription not found - additionalProperties: false - ValidationErrorList: - type: array - items: - $ref: "#/components/schemas/ValidationError" - ValidationError: - type: object - required: - - status - - code - - message - properties: - status: - type: string - enum: - - error - code: - type: string - pattern: ^[A-Z][A-Z0-9_]*$ - description: Machine-readable catalog code. - example: COMMON_VALIDATION_ERROR - message: - type: string - example: Input validation failed. - errors: - type: array - description: Per-field validation errors. - items: - type: object - required: - - field - - message - properties: - field: - type: string - message: - type: string - additionalProperties: false - additionalProperties: false OrganizationResponse: type: object properties: @@ -4418,7 +4358,7 @@ components: properties: name: type: string - apiTitle: + title: type: string nullable: true remotes: @@ -4885,10 +4825,10 @@ components: SubscriptionCreateRequest: type: object required: - - apiId + - artifactId - subscriptionPlanId properties: - apiId: + artifactId: type: string description: Developer Portal API ID. example: weather-api-v1 @@ -4914,7 +4854,7 @@ components: required: - planId properties: - apiId: + artifactId: type: string description: >- Developer Portal API ID the subscription belongs to. Optional — if provided, it is validated @@ -4934,7 +4874,7 @@ components: subscriptionId: type: string example: sub-12345 - apiId: + artifactId: type: string description: Developer Portal API ID. example: weather-api-v1 diff --git a/portals/developer-portal/docs/publish-apis/api-content-and-docs.md b/portals/developer-portal/docs/publish-apis/api-content-and-docs.md index 0bfa7fcb78..47f084a4d4 100644 --- a/portals/developer-portal/docs/publish-apis/api-content-and-docs.md +++ b/portals/developer-portal/docs/publish-apis/api-content-and-docs.md @@ -36,7 +36,7 @@ At least one of `web/` or `docs/` must be present in the ZIP. curl -k -X POST \ "https://localhost:3000/api/v0.9/apis/{apiId}/assets" \ -H "Authorization: Bearer $TOKEN" \ - -F "apiContent=@my-api-content.zip" \ + -F "content=@my-api-content.zip" \ -F 'imageMetadata={"api-icon":"api-icon.png"}' ``` @@ -46,13 +46,13 @@ To update existing content, use `PUT`: curl -k -X PUT \ "https://localhost:3000/api/v0.9/apis/{apiId}/assets" \ -H "Authorization: Bearer $TOKEN" \ - -F "apiContent=@my-api-content-v2.zip" \ + -F "content=@my-api-content-v2.zip" \ -F 'imageMetadata={"api-icon":"api-icon.png"}' ``` | Form field | Description | |---|---| -| `apiContent` | ZIP file containing `web/` and/or `docs/` directories | +| `content` | ZIP file containing `web/` and/or `docs/` directories | | `imageMetadata` | JSON object mapping image tag names to filenames within `web/`. The `api-icon` tag sets the catalog card icon | | `docMetadata` | JSON array for external documentation links (see [External Doc Links](#external-doc-links)) | @@ -140,7 +140,7 @@ To link to externally hosted documentation (rather than uploaded files), pass `d curl -k -X POST \ "https://localhost:3000/api/v0.9/apis/{apiId}/assets" \ -H "Authorization: Bearer $TOKEN" \ - -F "apiContent=@my-api-content.zip" \ + -F "content=@my-api-content.zip" \ -F 'docMetadata=[{"name":"External Guide","url":"https://docs.example.com/guide","type":"LINK"}]' ``` diff --git a/portals/developer-portal/it/rest-api/apis/artifact-zip-apis.spec.js b/portals/developer-portal/it/rest-api/apis/artifact-zip-apis.spec.js index 17911b965a..8f1a7fd4ed 100644 --- a/portals/developer-portal/it/rest-api/apis/artifact-zip-apis.spec.js +++ b/portals/developer-portal/it/rest-api/apis/artifact-zip-apis.spec.js @@ -17,10 +17,10 @@ // -------------------------------------------------------------------- // POST/PUT /apis via the `artifact` full-ZIP upload path (as opposed to the JSON -// `apiMetadata` field or the standalone `api`/`apiDefinition` YAML pair) — see +// `metadata` field or the standalone `api`/`definition` YAML pair) — see // apiMetadataService.js's extractFullApiBundleFromUploadedZip. A ZIP must contain // one of api.yaml/mcp.yaml/devportal.yaml plus a definition file -// (definition.(yaml|yml|json) or apiDefinition.(yaml|yml|json)); `web`/`docs` +// (definition.(yaml|yml|json)); `web`/`docs` // directories are optional (extractApiContentFromUploadedZip's 'artifact' mode // tolerates neither being present). @@ -109,7 +109,7 @@ describe('APIs via artifact ZIP upload', () => { // (metadata.name in the YAML, not spec.displayName) and the API's own `id` must // stay stable across the round-trip — apiDao.update() only recomputes the handle // from name+version when the request supplies no handle at all; the YAML path - // always supplies one via `metadata.name`, so unlike the JSON apiMetadata field + // always supplies one via `metadata.name`, so unlike the JSON metadata field // path this doesn't risk drifting the id/handle as a side effect of the update. it('creates via zip then updates the same API with the same zip plus a small change', async () => { const { handle, zip: firstZip } = buildZip({ displayName: 'Stable Zip API', description: 'version one' }); diff --git a/portals/developer-portal/it/rest-api/apis/graphql-apis.spec.js b/portals/developer-portal/it/rest-api/apis/graphql-apis.spec.js index 81192312a8..5e0d5fcb80 100644 --- a/portals/developer-portal/it/rest-api/apis/graphql-apis.spec.js +++ b/portals/developer-portal/it/rest-api/apis/graphql-apis.spec.js @@ -17,7 +17,7 @@ // -------------------------------------------------------------------- // POST/GET/PUT/DELETE /apis (type: GRAPHQL). Unlike REST/SOAP, GraphQL takes a -// `schemaDefinition` multipart field in addition to (or instead of) apiDefinition. +// `definition` multipart field carrying the SDL. const client = require('../support/client'); const { uniqueHandle } = require('../support/fixtures'); @@ -40,8 +40,8 @@ async function createGraphQLApi(overrides = {}) { const res = await client .as('publisher') .postMultipart('/apis') - .field('apiMetadata', JSON.stringify(metadata)) - .attach('schemaDefinition', Buffer.from(overrides.schema || SAMPLE_SCHEMA), 'schema.graphql'); + .field('metadata', JSON.stringify(metadata)) + .attach('definition', Buffer.from(overrides.schema || SAMPLE_SCHEMA), 'schema.graphql'); if (res.status !== 201) { throw new Error(`Failed to seed GraphQL API: ${res.status} ${JSON.stringify(res.body)}`); } @@ -53,7 +53,7 @@ describe('GraphQL APIs', () => { await client.login('publisher'); }); - it('creates a GraphQL API with a schemaDefinition file', async () => { + it('creates a GraphQL API with a definition file', async () => { const api = await createGraphQLApi(); const res = await client.as('publisher').get(`/apis/${api.id}`); expect(res.status).toBe(200); @@ -65,14 +65,14 @@ describe('GraphQL APIs', () => { const put = await client .as('publisher') .putMultipart(`/apis/${api.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ name: 'Updated GraphQL API', version: 'v1.0', type: 'GRAPHQL', status: 'PUBLISHED', endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('schemaDefinition', Buffer.from('type Query { goodbye: String }'), 'schema.graphql'); + .attach('definition', Buffer.from('type Query { goodbye: String }'), 'schema.graphql'); expect(put.status).toBe(200); expect(put.body.name).toBe('Updated GraphQL API'); }); @@ -86,12 +86,12 @@ describe('GraphQL APIs', () => { expect(get.status).toBe(404); }); - it('rejects a GraphQL API created without a schemaDefinition or apiDefinition', async () => { + it('rejects a GraphQL API created without a definition', async () => { const id = uniqueHandle('graphql-api-no-schema'); const res = await client .as('publisher') .postMultipart('/apis') - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id, name: 'No Schema API', version: 'v1.0', @@ -104,13 +104,13 @@ describe('GraphQL APIs', () => { it('retrieves the stored GraphQL schema', async () => { // The uploaded file's own name ('schema.graphql') isn't what's persisted — the - // schema is always stored under the fixed name apiDefinition.graphql and + // schema is always stored under the fixed name definition.graphql and // type=API_DEFINITION (apiMetadataService.js's createAPIMetadata, using // constants.FILE_NAME.API_DEFINITION_GRAPHQL), same category REST/SOAP // definitions use. const schema = 'type Query { hello: String }'; const api = await createGraphQLApi({ schema }); - const res = await client.as('publisher').get(`/apis/${api.id}/assets?type=API_DEFINITION&fileName=apiDefinition.graphql`); + const res = await client.as('publisher').get(`/apis/${api.id}/assets?type=API_DEFINITION&fileName=definition.graphql`); expect(res.status).toBe(200); // .graphql isn't in util.isTextFile's allowlist, so it's served with a generic // binary content-type rather than text/plain — read whichever field supertest @@ -124,7 +124,7 @@ describe('GraphQL APIs', () => { const res = await client .as('publisher') .postMultipart('/apis') - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id, name: 'No File API', version: 'v1.0', @@ -132,7 +132,7 @@ describe('GraphQL APIs', () => { status: 'PUBLISHED', endPoints: { productionURL: 'https://x.invalid', sandboxURL: 'https://x.invalid' }, })) - .attach('schemaDefinition', Buffer.from('type Query { hello: String }'), 'schema.graphql'); + .attach('definition', Buffer.from('type Query { hello: String }'), 'schema.graphql'); expect(res.status).toBe(201); const get = await client.as('publisher').get(`/apis/${res.body.id}/assets?type=API_DEFINITION&fileName=does-not-exist.graphql`); diff --git a/portals/developer-portal/it/rest-api/apis/rest-apis.spec.js b/portals/developer-portal/it/rest-api/apis/rest-apis.spec.js index f4d5822ed5..3688984ca2 100644 --- a/portals/developer-portal/it/rest-api/apis/rest-apis.spec.js +++ b/portals/developer-portal/it/rest-api/apis/rest-apis.spec.js @@ -17,7 +17,7 @@ // -------------------------------------------------------------------- // POST/GET/PUT/DELETE /apis (type: REST). See docs/devportal-openapi-spec-v0.9.yaml -// ApiMetadataMultipartBody — creation is multipart (apiMetadata JSON + apiDefinition +// ApiMetadataMultipartBody — creation is multipart (metadata JSON + definition // file). Publisher owns API management; follow support/fixtures.js createApi() // for the request shape; organizations.spec.js for the assertion style. @@ -53,14 +53,14 @@ describe('REST APIs', () => { const put = await client .as('publisher') .putMultipart(`/apis/${api.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ name: 'Updated Name', version: 'v1.0', type: 'REST', status: 'PUBLISHED', endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('apiDefinition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); + .attach('definition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); expect(put.status).toBe(200); expect(put.body.name).toBe('Updated Name'); }); @@ -78,7 +78,7 @@ describe('REST APIs', () => { const name = uniqueHandle('Filterable API'); await createApi({ type: 'REST', name, version: 'v2.0' }); - const res = await client.as('publisher').get(`/apis?apiName=${encodeURIComponent(name)}&version=v2.0`); + const res = await client.as('publisher').get(`/apis?name=${encodeURIComponent(name)}&version=v2.0`); expect(res.status).toBe(200); expect(res.body.list.some((a) => a.name === name)).toBe(true); }); @@ -88,7 +88,7 @@ describe('REST APIs', () => { const res = await client .as('publisher') .postMultipart('/apis') - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id, name: 'Bad API', version: 'v1.0', @@ -96,7 +96,7 @@ describe('REST APIs', () => { status: 'PUBLISHED', endPoints: { productionURL: 'https://x.invalid', sandboxURL: 'https://x.invalid' }, })) - .attach('apiDefinition', Buffer.from('not a valid openapi document'), 'definition.json'); + .attach('definition', Buffer.from('not a valid openapi document'), 'definition.json'); expect(res.status).toBe(400); }); @@ -105,14 +105,14 @@ describe('REST APIs', () => { const res = await client .as('publisher') .postMultipart('/apis') - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id, name: 'No Type API', version: 'v1.0', status: 'PUBLISHED', endPoints: { productionURL: 'https://x.invalid', sandboxURL: 'https://x.invalid' }, })) - .attach('apiDefinition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); + .attach('definition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); expect(res.status).toBe(400); }); @@ -121,13 +121,13 @@ describe('REST APIs', () => { const put = await client .as('publisher') .putMultipart(`/apis/${api.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ name: 'Updated Without Type', version: 'v1.0', status: 'PUBLISHED', endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('apiDefinition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); + .attach('definition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); expect(put.status).toBe(400); }); @@ -139,14 +139,14 @@ describe('REST APIs', () => { const put = await client .as('publisher') .putMultipart(`/apis/${api.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ name: 'Should Stay REST', version: 'v1.0', type: 'SOAP', status: 'PUBLISHED', endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('apiDefinition', Buffer.from(''), 'definition.wsdl'); + .attach('definition', Buffer.from(''), 'definition.wsdl'); expect(put.status).toBe(409); const get = await client.as('publisher').get(`/apis/${api.id}`); @@ -165,7 +165,7 @@ describe('REST APIs', () => { const res = await client .as('publisher') .postMultipart('/apis') - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id, name: 'Should Be MCP', version: 'v1.0', @@ -173,7 +173,7 @@ describe('REST APIs', () => { status: 'PUBLISHED', endPoints: { productionURL: 'https://x.invalid', sandboxURL: 'https://x.invalid' }, })) - .attach('apiDefinition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); + .attach('definition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); expect(res.status).toBe(400); }); @@ -183,7 +183,7 @@ describe('REST APIs', () => { const res = await client .as('developer') .postMultipart('/apis') - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id, name: 'Should Be Forbidden', version: 'v1.0', @@ -191,13 +191,13 @@ describe('REST APIs', () => { status: 'PUBLISHED', endPoints: { productionURL: 'https://x.invalid', sandboxURL: 'https://x.invalid' }, })) - .attach('apiDefinition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); + .attach('definition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); expect(res.status).toBe(403); }); it('uploads and retrieves an OpenAPI definition', async () => { // createApi()'s default upload (support/fixtures.js) — fileName must match - // what was actually persisted, not a hardcoded 'apiDefinition.json': the file + // what was actually persisted, not a hardcoded 'definition.json': the file // keeps its original uploaded name ('definition.json') under type=API_DEFINITION // (src/utils/constants.js DOC_TYPES.API_DEFINITION). const api = await createApi({ type: 'REST' }); @@ -229,7 +229,7 @@ describe('REST APIs', () => { describe('label updates', () => { // apiDao.update() has no `labels` column — labels only ever change via the // add/remove diff computed in updateAPIMetadata, which used to only run for - // YAML/artifact uploads. A `labels` array sent via the plain JSON apiMetadata + // YAML/artifact uploads. A `labels` array sent via the plain JSON metadata // field (what every other update test in this suite uses) was a silent no-op. // // NOTE: every PUT body here includes `id: api.id`. Without it, apiDao.update() @@ -248,7 +248,7 @@ describe('REST APIs', () => { const put = await client .as('publisher') .putMultipart(`/apis/${api.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id: api.id, name: api.name, version: api.version, @@ -257,7 +257,7 @@ describe('REST APIs', () => { labels: [labelB.id], endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('apiDefinition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); + .attach('definition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); expect(put.status).toBe(200); const get = await client.as('publisher').get(`/apis/${api.id}`); @@ -271,7 +271,7 @@ describe('REST APIs', () => { const put = await client .as('publisher') .putMultipart(`/apis/${api.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id: api.id, name: 'Same Labels Resent', version: api.version, @@ -280,7 +280,7 @@ describe('REST APIs', () => { labels: [label.id], endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('apiDefinition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); + .attach('definition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); expect(put.status).toBe(200); const get = await client.as('publisher').get(`/apis/${api.id}`); @@ -297,7 +297,7 @@ describe('REST APIs', () => { const put = await client .as('publisher') .putMultipart(`/apis/${api.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id: api.id, name: api.name, version: api.version, @@ -306,7 +306,7 @@ describe('REST APIs', () => { labels: [label.id], endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('apiDefinition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); + .attach('definition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); expect(put.status).toBe(200); const get = await client.as('publisher').get(`/apis/${api.id}`); @@ -320,7 +320,7 @@ describe('REST APIs', () => { const put = await client .as('publisher') .putMultipart(`/apis/${api.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id: api.id, name: api.name, version: api.version, @@ -329,7 +329,7 @@ describe('REST APIs', () => { labels: [], endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('apiDefinition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); + .attach('definition', Buffer.from(JSON.stringify({ openapi: '3.0.3', info: { title: 'x', version: '1' }, paths: {} })), 'definition.json'); expect(put.status).toBe(200); const get = await client.as('publisher').get(`/apis/${api.id}`); diff --git a/portals/developer-portal/it/rest-api/apis/soap-apis.spec.js b/portals/developer-portal/it/rest-api/apis/soap-apis.spec.js index 1b1943da8c..54e8b374db 100644 --- a/portals/developer-portal/it/rest-api/apis/soap-apis.spec.js +++ b/portals/developer-portal/it/rest-api/apis/soap-apis.spec.js @@ -16,7 +16,7 @@ // under the License. // -------------------------------------------------------------------- -// POST/GET/PUT/DELETE /apis (type: SOAP). apiDefinition is a WSDL file instead +// POST/GET/PUT/DELETE /apis (type: SOAP). definition is a WSDL file instead // of an OpenAPI document. const client = require('../support/client'); @@ -41,14 +41,14 @@ describe('SOAP APIs', () => { const put = await client .as('publisher') .putMultipart(`/apis/${api.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ name: 'Updated SOAP API', version: 'v1.0', type: 'SOAP', status: 'PUBLISHED', endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('apiDefinition', Buffer.from(SAMPLE_WSDL), 'definition.wsdl'); + .attach('definition', Buffer.from(SAMPLE_WSDL), 'definition.wsdl'); expect(put.status).toBe(200); expect(put.body.name).toBe('Updated SOAP API'); }); @@ -67,7 +67,7 @@ describe('SOAP APIs', () => { const res = await client .as('publisher') .postMultipart('/apis') - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id, name: 'No Definition SOAP API', version: 'v1.0', diff --git a/portals/developer-portal/it/rest-api/apis/websocket-apis.spec.js b/portals/developer-portal/it/rest-api/apis/websocket-apis.spec.js index 1c9069ed62..41edfcba48 100644 --- a/portals/developer-portal/it/rest-api/apis/websocket-apis.spec.js +++ b/portals/developer-portal/it/rest-api/apis/websocket-apis.spec.js @@ -17,7 +17,7 @@ // -------------------------------------------------------------------- // POST/GET/PUT/DELETE /apis (type: WS). createAPIMetadata still requires *some* -// apiDefinition file for every non-GraphQL type (src/services/apiMetadataService.js +// definition file for every non-GraphQL type (src/services/apiMetadataService.js // validation), even though WS has no real schema concept — the fixture just // attaches a placeholder file to satisfy that. @@ -41,14 +41,14 @@ describe('WebSocket APIs', () => { const put = await client .as('publisher') .putMultipart(`/apis/${api.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ name: 'Updated WS API', version: 'v1.0', type: 'WS', status: 'PUBLISHED', endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('apiDefinition', Buffer.from('{}'), 'definition.json'); + .attach('definition', Buffer.from('{}'), 'definition.json'); expect(put.status).toBe(200); expect(put.body.name).toBe('Updated WS API'); }); diff --git a/portals/developer-portal/it/rest-api/apis/websub-apis.spec.js b/portals/developer-portal/it/rest-api/apis/websub-apis.spec.js index 327237136d..949f520807 100644 --- a/portals/developer-portal/it/rest-api/apis/websub-apis.spec.js +++ b/portals/developer-portal/it/rest-api/apis/websub-apis.spec.js @@ -40,14 +40,14 @@ describe('WebSub APIs', () => { const put = await client .as('publisher') .putMultipart(`/apis/${api.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ name: 'Updated WebSub API', version: 'v1.0', type: 'WEBSUB', status: 'PUBLISHED', endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('apiDefinition', Buffer.from('{}'), 'definition.json'); + .attach('definition', Buffer.from('{}'), 'definition.json'); expect(put.status).toBe(200); expect(put.body.name).toBe('Updated WebSub API'); }); diff --git a/portals/developer-portal/it/rest-api/mcp-servers/mcp-servers.spec.js b/portals/developer-portal/it/rest-api/mcp-servers/mcp-servers.spec.js index c72d8640cc..7c39d520ed 100644 --- a/portals/developer-portal/it/rest-api/mcp-servers/mcp-servers.spec.js +++ b/portals/developer-portal/it/rest-api/mcp-servers/mcp-servers.spec.js @@ -25,7 +25,7 @@ const client = require('../support/client'); const { uniqueHandle, createApi } = require('../support/fixtures'); -// An MCP server's contract is its tools schema (schemaDefinition) — it has no apiDefinition. +// An MCP server's contract is its tools schema (definition) — not an OpenAPI-style API contract. const MCP_TOOLS_SCHEMA = [ '- type: TOOL', ' name: ping', @@ -51,8 +51,8 @@ async function createMcpServer(overrides = {}) { const res = await client .as('publisher') .postMultipart('/mcp-servers') - .field('apiMetadata', JSON.stringify(metadata)) - .attach('schemaDefinition', Buffer.from(MCP_TOOLS_SCHEMA), 'schemaDefinition.yaml'); + .field('metadata', JSON.stringify(metadata)) + .attach('definition', Buffer.from(MCP_TOOLS_SCHEMA), 'definition.yaml'); if (res.status !== 201) { throw new Error(`Failed to seed MCP server: ${res.status} ${JSON.stringify(res.body)}`); } @@ -76,14 +76,14 @@ describe('MCP servers', () => { const res = await client .as('publisher') .postMultipart('/mcp-servers') - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id, name: 'Omitted Type Test', version: 'v1.0', status: 'PUBLISHED', endPoints: { productionURL: 'https://x.invalid', sandboxURL: 'https://x.invalid' }, })) - .attach('schemaDefinition', Buffer.from(MCP_TOOLS_SCHEMA), 'schemaDefinition.yaml'); + .attach('definition', Buffer.from(MCP_TOOLS_SCHEMA), 'definition.yaml'); expect(res.status).toBe(400); }); @@ -95,7 +95,7 @@ describe('MCP servers', () => { const res = await client .as('publisher') .postMultipart('/mcp-servers') - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id, name: 'Wrong Endpoint', version: 'v1.0', @@ -103,7 +103,7 @@ describe('MCP servers', () => { status: 'PUBLISHED', endPoints: { productionURL: 'https://x.invalid', sandboxURL: 'https://x.invalid' }, })) - .attach('schemaDefinition', Buffer.from(MCP_TOOLS_SCHEMA), 'schemaDefinition.yaml'); + .attach('definition', Buffer.from(MCP_TOOLS_SCHEMA), 'definition.yaml'); expect(res.status).toBe(400); }); @@ -112,14 +112,14 @@ describe('MCP servers', () => { const put = await client .as('publisher') .putMultipart(`/mcp-servers/${mcp.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ name: 'Should Stay MCP', version: 'v1.0', type: 'REST', status: 'PUBLISHED', endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('schemaDefinition', Buffer.from(MCP_TOOLS_SCHEMA), 'schemaDefinition.yaml'); + .attach('definition', Buffer.from(MCP_TOOLS_SCHEMA), 'definition.yaml'); expect(put.status).toBe(400); }); @@ -128,14 +128,14 @@ describe('MCP servers', () => { const put = await client .as('publisher') .putMultipart(`/mcp-servers/${mcp.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ name: 'Updated MCP Server', version: 'v1.0', type: 'MCP', status: 'PUBLISHED', endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('schemaDefinition', Buffer.from(MCP_TOOLS_SCHEMA), 'schemaDefinition.yaml'); + .attach('definition', Buffer.from(MCP_TOOLS_SCHEMA), 'definition.yaml'); expect(put.status).toBe(200); expect(put.body.name).toBe('Updated MCP Server'); }); @@ -145,13 +145,13 @@ describe('MCP servers', () => { const put = await client .as('publisher') .putMultipart(`/mcp-servers/${mcp.id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ name: 'Updated MCP Server', version: 'v1.0', status: 'PUBLISHED', endPoints: { productionURL: 'https://updated.example.invalid', sandboxURL: 'https://updated-sandbox.example.invalid' }, })) - .attach('schemaDefinition', Buffer.from(MCP_TOOLS_SCHEMA), 'schemaDefinition.yaml'); + .attach('definition', Buffer.from(MCP_TOOLS_SCHEMA), 'definition.yaml'); expect(put.status).toBe(400); }); @@ -168,7 +168,7 @@ describe('MCP servers', () => { const name = uniqueHandle('Listable MCP Server'); await createMcpServer({ name }); - const res = await client.as('publisher').get(`/mcp-servers?apiName=${encodeURIComponent(name)}`); + const res = await client.as('publisher').get(`/mcp-servers?name=${encodeURIComponent(name)}`); expect(res.status).toBe(200); expect(res.body.list.some((m) => m.name === name)).toBe(true); }); @@ -178,7 +178,7 @@ describe('MCP servers', () => { const res = await client .as('publisher') .postMultipart('/apis') - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id, name: 'Wrong Endpoint', version: 'v1.0', @@ -186,7 +186,7 @@ describe('MCP servers', () => { status: 'PUBLISHED', endPoints: { productionURL: 'https://x.invalid', sandboxURL: 'https://x.invalid' }, })) - .attach('apiDefinition', Buffer.from(JSON.stringify({ openapi: '3.0.0' })), 'definition.json'); + .attach('definition', Buffer.from(JSON.stringify({ openapi: '3.0.0' })), 'definition.json'); expect(res.status).toBe(400); }); @@ -199,17 +199,17 @@ describe('MCP servers', () => { expect(res.body.key).toBeDefined(); }); - // An MCP server's contract is its tools schema (schemaDefinition), analogous to a - // GraphQL SDL — not an apiDefinition. It can be created from a schemaDefinition alone - // with no apiDefinition, matching how sampleSeeder deploys samples/mcps (api.yaml + - // schemaDefinition.yaml, no definition file) and how the admin UI wizard now uploads it. - describe('tools schema (schemaDefinition)', () => { + // An MCP server's contract is its tools schema, supplied via the single `definition` + // field — analogous to a GraphQL SDL, not an OpenAPI-style API contract. It is created + // from that definition alone, matching how sampleSeeder deploys samples/mcps (api.yaml + + // definition.yaml) and how the admin UI wizard now uploads it. + describe('tools schema (definition)', () => { function createWithSchema(schema, idPrefix) { const id = uniqueHandle(idPrefix || 'mcp-schema'); return client .as('publisher') .postMultipart('/mcp-servers') - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id, name: `Schema MCP ${id}`, version: 'v1.0', @@ -217,27 +217,27 @@ describe('MCP servers', () => { status: 'PUBLISHED', endPoints: { productionURL: 'https://x.invalid', sandboxURL: 'https://x.invalid' }, })) - .attach('schemaDefinition', Buffer.from(schema), 'schemaDefinition.yaml') + .attach('definition', Buffer.from(schema), 'definition.yaml') .then((res) => ({ id, res })); } - it('creates an MCP server from a schemaDefinition with no apiDefinition, stored as SCHEMA_DEFINITION', async () => { + it('creates an MCP server from a definition, stored as SCHEMA_DEFINITION', async () => { const { id, res } = await createWithSchema(MCP_TOOLS_SCHEMA); expect(res.status).toBe(201); // The tools schema is persisted and retrievable as a SCHEMA_DEFINITION asset. const asset = await client.as('publisher') - .get(`/mcp-servers/${id}/assets?type=SCHEMA_DEFINITION&fileName=schemaDefinition.yaml`); + .get(`/mcp-servers/${id}/assets?type=SCHEMA_DEFINITION&fileName=definition.yaml`); expect(asset.status).toBe(200); expect(String(asset.text || asset.body)).toContain('name: ping'); }); - it('rejects creating an MCP server without a schemaDefinition', async () => { + it('rejects creating an MCP server without a definition', async () => { const id = uniqueHandle('mcp-no-contract'); const res = await client .as('publisher') .postMultipart('/mcp-servers') - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ id, name: 'No Contract MCP', version: 'v1.0', @@ -248,24 +248,7 @@ describe('MCP servers', () => { expect(res.status).toBe(400); }); - it('rejects creating an MCP server with an apiDefinition but no schemaDefinition (apiDefinition is not an MCP contract)', async () => { - const id = uniqueHandle('mcp-apidef-only'); - const res = await client - .as('publisher') - .postMultipart('/mcp-servers') - .field('apiMetadata', JSON.stringify({ - id, - name: 'ApiDef Only MCP', - version: 'v1.0', - type: 'MCP', - status: 'PUBLISHED', - endPoints: { productionURL: 'https://x.invalid', sandboxURL: 'https://x.invalid' }, - })) - .attach('apiDefinition', Buffer.from(JSON.stringify({ openapi: '3.0.0' })), 'definition.json'); - expect(res.status).toBe(400); - }); - - it('replaces the tools schema on update via a new schemaDefinition', async () => { + it('replaces the tools schema on update via a new definition', async () => { const { id, res } = await createWithSchema(MCP_TOOLS_SCHEMA, 'mcp-schema-update'); expect(res.status).toBe(201); @@ -273,25 +256,25 @@ describe('MCP servers', () => { const put = await client .as('publisher') .putMultipart(`/mcp-servers/${id}`) - .field('apiMetadata', JSON.stringify({ + .field('metadata', JSON.stringify({ name: `Schema MCP ${id}`, version: 'v1.0', type: 'MCP', status: 'PUBLISHED', endPoints: { productionURL: 'https://x.invalid', sandboxURL: 'https://x.invalid' }, })) - .attach('schemaDefinition', Buffer.from(updatedSchema), 'schemaDefinition.yaml'); + .attach('definition', Buffer.from(updatedSchema), 'definition.yaml'); expect(put.status).toBe(200); const asset = await client.as('publisher') - .get(`/mcp-servers/${id}/assets?type=SCHEMA_DEFINITION&fileName=schemaDefinition.yaml`); + .get(`/mcp-servers/${id}/assets?type=SCHEMA_DEFINITION&fileName=definition.yaml`); expect(asset.status).toBe(200); expect(String(asset.text || asset.body)).toContain('name: echo'); }); }); // A server created via the admin /mcp-servers API stores its schema as a flat - // `type:`-tagged YAML array (schemaDefinition.yaml). The MCP Registry API must still + // `type:`-tagged YAML array (definition.yaml). The MCP Registry API must still // surface those capabilities — mcpRegistryService.parseSchema normalizes the flat array // into the grouped { tools, resources, prompts } shape the registry response expects. describe('MCP registry exposure of admin-created servers', () => { @@ -335,7 +318,7 @@ describe('MCP servers', () => { const name = uniqueHandle('Should Not Appear In MCP List'); await createApi({ name }); - const res = await client.as('publisher').get(`/mcp-servers?apiName=${encodeURIComponent(name)}`); + const res = await client.as('publisher').get(`/mcp-servers?name=${encodeURIComponent(name)}`); expect(res.status).toBe(200); expect(res.body.list.some((m) => m.name === name)).toBe(false); }); @@ -344,7 +327,7 @@ describe('MCP servers', () => { const name = uniqueHandle('Should Not Appear In Apis List'); await createMcpServer({ name }); - const res = await client.as('publisher').get(`/apis?apiName=${encodeURIComponent(name)}`); + const res = await client.as('publisher').get(`/apis?name=${encodeURIComponent(name)}`); expect(res.status).toBe(200); expect(res.body.list.some((a) => a.name === name)).toBe(false); }); diff --git a/portals/developer-portal/it/rest-api/subscriptions/subscriptions.spec.js b/portals/developer-portal/it/rest-api/subscriptions/subscriptions.spec.js index ca9bdc610b..10f448a778 100644 --- a/portals/developer-portal/it/rest-api/subscriptions/subscriptions.spec.js +++ b/portals/developer-portal/it/rest-api/subscriptions/subscriptions.spec.js @@ -39,23 +39,23 @@ describe('subscriptions', () => { }); it('creates a subscription for an application to an API plan', async () => { - const res = await client.as('developer').post('/subscriptions', { apiId: api.id, subscriptionPlanId: 'Gold' }); + const res = await client.as('developer').post('/subscriptions', { artifactId: api.id, subscriptionPlanId: 'Gold' }); expect(res.status).toBe(201); - expect(res.body.apiId).toBe(api.id); + expect(res.body.artifactId).toBe(api.id); expect(res.body.subscriptionPlanName).toBe('Gold'); expect(res.body.status).toBe('ACTIVE'); expect(res.body.subscriptionToken).toBeDefined(); }); it('retrieves a subscription', async () => { - const create = await client.as('developer').post('/subscriptions', { apiId: api.id, subscriptionPlanId: 'Gold' }); + const create = await client.as('developer').post('/subscriptions', { artifactId: api.id, subscriptionPlanId: 'Gold' }); const res = await client.as('developer').get(`/subscriptions/${create.body.subscriptionId}`); expect(res.status).toBe(200); - expect(res.body.apiId).toBe(api.id); + expect(res.body.artifactId).toBe(api.id); }); it('changes a subscription plan', async () => { - const create = await client.as('developer').post('/subscriptions', { apiId: api.id, subscriptionPlanId: 'Gold' }); + const create = await client.as('developer').post('/subscriptions', { artifactId: api.id, subscriptionPlanId: 'Gold' }); const res = await client.as('developer').post(`/subscriptions/${create.body.subscriptionId}/change-plan`, { planId: 'Silver' }); expect(res.status).toBe(200); expect(res.body.subscriptionPlanName).toBe('Silver'); @@ -64,7 +64,7 @@ describe('subscriptions', () => { }); it('regenerates a subscription token', async () => { - const create = await client.as('developer').post('/subscriptions', { apiId: api.id, subscriptionPlanId: 'Gold' }); + const create = await client.as('developer').post('/subscriptions', { artifactId: api.id, subscriptionPlanId: 'Gold' }); const res = await client.as('developer').post(`/subscriptions/${create.body.subscriptionId}/regenerate-token`, {}); expect(res.status).toBe(200); expect(res.body.subscriptionToken).toBeDefined(); @@ -72,7 +72,7 @@ describe('subscriptions', () => { }); it('deletes a subscription', async () => { - const create = await client.as('developer').post('/subscriptions', { apiId: api.id, subscriptionPlanId: 'Gold' }); + const create = await client.as('developer').post('/subscriptions', { artifactId: api.id, subscriptionPlanId: 'Gold' }); const del = await client.as('developer').del(`/subscriptions/${create.body.subscriptionId}`); expect(del.status).toBe(200); @@ -81,17 +81,17 @@ describe('subscriptions', () => { }); it('rejects subscribing with a plan not linked to the API', async () => { - const res = await client.as('developer').post('/subscriptions', { apiId: api.id, subscriptionPlanId: 'Bronze' }); + const res = await client.as('developer').post('/subscriptions', { artifactId: api.id, subscriptionPlanId: 'Bronze' }); expect(res.status).toBe(400); }); it('rejects subscribing to a non-existent API', async () => { - const res = await client.as('developer').post('/subscriptions', { apiId: 'does-not-exist', subscriptionPlanId: 'Gold' }); + const res = await client.as('developer').post('/subscriptions', { artifactId: 'does-not-exist', subscriptionPlanId: 'Gold' }); expect(res.status).toBe(404); }); it('rejects change-plan to a plan that does not exist in the org', async () => { - const create = await client.as('developer').post('/subscriptions', { apiId: api.id, subscriptionPlanId: 'Gold' }); + const create = await client.as('developer').post('/subscriptions', { artifactId: api.id, subscriptionPlanId: 'Gold' }); const res = await client.as('developer').post(`/subscriptions/${create.body.subscriptionId}/change-plan`, { planId: 'DoesNotExist' }); expect(res.status).toBe(400); }); diff --git a/portals/developer-portal/it/rest-api/support/fixtures.js b/portals/developer-portal/it/rest-api/support/fixtures.js index 65ed08bac0..a958158e85 100644 --- a/portals/developer-portal/it/rest-api/support/fixtures.js +++ b/portals/developer-portal/it/rest-api/support/fixtures.js @@ -76,7 +76,7 @@ const MINIMAL_OPENAPI_DEFINITION = JSON.stringify({ paths: { '/ping': { get: { responses: { 200: { description: 'ok' } } } } }, }); -// POST /apis takes multipart/form-data: `apiMetadata` (JSON string) + `apiDefinition` +// POST /apis takes multipart/form-data: `metadata` (JSON string) + `definition` // (file) — see docs/devportal-openapi-spec-v0.9.yaml ApiMetadataMultipartBody. // `publisher` holds the API-management scopes; pass `role` to override. async function createApi(overrides = {}) { @@ -103,8 +103,8 @@ async function createApi(overrides = {}) { const res = await client .as(role) .postMultipart('/apis') - .field('apiMetadata', JSON.stringify(metadata)) - .attach('apiDefinition', Buffer.from(overrides.definition || MINIMAL_OPENAPI_DEFINITION), definitionFileName); + .field('metadata', JSON.stringify(metadata)) + .attach('definition', Buffer.from(overrides.definition || MINIMAL_OPENAPI_DEFINITION), definitionFileName); if (res.status !== 201) { throw new Error(`Failed to seed API: ${res.status} ${JSON.stringify(res.body)}`); } diff --git a/portals/developer-portal/samples/mcps/travel-assistant-mcp-v1/schemaDefinition.yaml b/portals/developer-portal/samples/mcps/travel-assistant-mcp-v1/definition.yaml similarity index 100% rename from portals/developer-portal/samples/mcps/travel-assistant-mcp-v1/schemaDefinition.yaml rename to portals/developer-portal/samples/mcps/travel-assistant-mcp-v1/definition.yaml diff --git a/portals/developer-portal/scripts/drift_check.js b/portals/developer-portal/scripts/drift_check.js index 9a92f0c8b5..cd8447f0ac 100644 --- a/portals/developer-portal/scripts/drift_check.js +++ b/portals/developer-portal/scripts/drift_check.js @@ -265,7 +265,7 @@ const SAMPLES = [ // createAPIMetadata: `res.status(201).send({ ...apiMetadata, ...audit })` (line // ~247) — spreads the parsed request metadata (name, version, type, status, // agentVisibility, tags, labels, owners, endPoints, subscriptionPlans, the - // resolved `id`) plus audit fields. NOT an APIDTO — no `refId`/`apiTitle`/ + // resolved `id`) plus audit fields. NOT an APIDTO — no `refId`/`title`/ // `remotes`/`apiImageMetadata`, unlike get/update below. ['createApiMetadata', 201, { name: 'Zip API', version: 'v1.0', description: 'first', type: 'RestApi', @@ -278,7 +278,7 @@ const SAMPLES = [ // getApiMetadata: getMetadataFromDB wraps the DAO row in `new APIDTO(...)` // (apiDto.js) — a different, larger shape than create's. ['getApiMetadata', 200, { - id: 'zip-api-1', refId: null, name: 'Zip API', apiTitle: null, remotes: [], + id: 'zip-api-1', refId: null, name: 'Zip API', title: null, remotes: [], version: 'v1.0', description: 'first', type: 'RestApi', status: 'PUBLISHED', agentVisibility: 'VISIBLE', apiImageMetadata: {}, tags: [], labels: [], endPoints: { sandboxURL: 'https://sandbox.example.invalid/zip-api-1', productionURL: 'https://backend.example.invalid/zip-api-1' }, @@ -291,7 +291,7 @@ const SAMPLES = [ // `tags`/`labels` present when the update request didn't touch those fields // (APIDTO only sets what its input row/audit actually carried at that point). ['updateApiMetadata', 200, { - id: 'zip-api-1', refId: null, name: 'Zip API', apiTitle: null, remotes: [], + id: 'zip-api-1', refId: null, name: 'Zip API', title: null, remotes: [], version: 'v1.0', description: 'second (changed)', type: 'RestApi', status: 'PUBLISHED', agentVisibility: 'VISIBLE', endPoints: { sandboxURL: 'https://sandbox.example.invalid/zip-api-1', productionURL: 'https://backend.example.invalid/zip-api-1' }, @@ -303,7 +303,7 @@ const SAMPLES = [ // same per-item shape as getApiMetadata. ['getAllApiMetadataForOrganization', 200, { list: [{ - id: 'zip-api-1', refId: null, name: 'Zip API', apiTitle: null, remotes: [], + id: 'zip-api-1', refId: null, name: 'Zip API', title: null, remotes: [], version: 'v1.0', description: 'first', type: 'RestApi', status: 'PUBLISHED', agentVisibility: 'VISIBLE', apiImageMetadata: {}, tags: [], labels: ['default'], endPoints: { sandboxURL: 'https://sandbox.example.invalid/zip-api-1', productionURL: 'https://backend.example.invalid/zip-api-1' }, @@ -327,7 +327,7 @@ const SAMPLES = [ createdAt: '2026-07-07T12:48:30.875Z', updatedAt: '2026-07-07T12:48:30.875Z', }], ['getMcpServer', 200, { - id: 'mcp-server-1', refId: null, name: 'Booking MCP', apiTitle: null, remotes: [], + id: 'mcp-server-1', refId: null, name: 'Booking MCP', title: null, remotes: [], version: 'v1.0', description: 'first', type: 'Mcp', status: 'PUBLISHED', agentVisibility: 'VISIBLE', apiImageMetadata: {}, tags: [], labels: [], endPoints: { sandboxURL: 'https://sandbox.example.invalid/mcp-1', productionURL: 'https://backend.example.invalid/mcp-1' }, @@ -336,7 +336,7 @@ const SAMPLES = [ createdAt: '2026-07-07T12:48:30.875Z', updatedAt: '2026-07-07T12:48:30.875Z', }], ['updateMcpServer', 200, { - id: 'mcp-server-1', refId: null, name: 'Booking MCP', apiTitle: null, remotes: [], + id: 'mcp-server-1', refId: null, name: 'Booking MCP', title: null, remotes: [], version: 'v1.0', description: 'second (changed)', type: 'Mcp', status: 'PUBLISHED', agentVisibility: 'VISIBLE', endPoints: { sandboxURL: 'https://sandbox.example.invalid/mcp-1', productionURL: 'https://backend.example.invalid/mcp-1' }, @@ -346,7 +346,7 @@ const SAMPLES = [ }], ['getAllMcpServersForOrganization', 200, { list: [{ - id: 'mcp-server-1', refId: null, name: 'Booking MCP', apiTitle: null, remotes: [], + id: 'mcp-server-1', refId: null, name: 'Booking MCP', title: null, remotes: [], version: 'v1.0', description: 'first', type: 'Mcp', status: 'PUBLISHED', agentVisibility: 'VISIBLE', apiImageMetadata: {}, tags: [], labels: ['default'], endPoints: { sandboxURL: 'https://sandbox.example.invalid/mcp-1', productionURL: 'https://backend.example.invalid/mcp-1' }, diff --git a/portals/developer-portal/src/controllers/apiContentController.js b/portals/developer-portal/src/controllers/apiContentController.js index 110c0a62ad..dcec48a404 100644 --- a/portals/developer-portal/src/controllers/apiContentController.js +++ b/portals/developer-portal/src/controllers/apiContentController.js @@ -371,12 +371,30 @@ const loadAPIContent = async (req, res, next) => { isAdmin: req.user.isAdmin, } } - let schemaFileName = constants.FILE_NAME.API_DEFINITION_XML; - if (metaData.type === constants.API_TYPE.GRAPHQL) { - schemaFileName = constants.FILE_NAME.API_DEFINITION_GRAPHQL; - } else if (metaData.type === constants.API_TYPE.MCP) { - // An MCP server's contract is its tools schema, not an apiDefinition. - schemaFileName = constants.FILE_NAME.SCHEMA_DEFINITION_YAML_FILE_NAME; + // Build the definition download link from the file that is actually stored, resolved + // by its doc-type (one definition per API). The stored name is the uploaded basename, + // not a fixed constant, so guessing a filename here would 404 for any file uploaded + // under a different name (e.g. a SOAP WSDL named 'service.wsdl'). MCP tools schemas live + // under SCHEMA_DEFINITION and are served from /mcp-servers; everything else under + // API_DEFINITION from /apis. + const definitionDocType = metaData.type === constants.API_TYPE.MCP + ? constants.DOC_TYPES.SCHEMA_DEFINITION + : constants.DOC_TYPES.API_DEFINITION; + const definitionAssetBasePath = metaData.type === constants.API_TYPE.MCP + ? '/mcp-servers/' + : constants.ROUTE.API_FILE_PATH; + let schemaUrl = null; + try { + const definitionDoc = await apiFileDao.getByType(definitionDocType, orgId, apiId); + if (definitionDoc?.file_name) { + schemaUrl = `${req.protocol}://${req.get('host')}${constants.DEVPORTAL_API.orgPath(orgId)}` + + `${definitionAssetBasePath}${apiId}/assets?type=${definitionDocType}` + + `&fileName=${encodeURIComponent(definitionDoc.file_name)}`; + } + } catch (schemaErr) { + logger.debug('Could not resolve definition file for download link', { + orgId, apiId, error: schemaErr.message + }); } let apiDefinitionForNav = null; @@ -398,7 +416,7 @@ const loadAPIContent = async (req, res, next) => { subscriptionPlans: subscriptionPlans, subscriptions: subscriptions, baseUrl: '/' + orgName + constants.ROUTE.VIEWS_PATH + viewName, - schemaUrl: `${req.protocol}://${req.get('host')}${constants.DEVPORTAL_API.orgPath(orgId)}${constants.ROUTE.API_FILE_PATH}${apiId}${constants.API_TEMPLATE_FILE_NAME}${schemaFileName}`, + schemaUrl: schemaUrl, loadDefault: loadDefault, resources: apiDetails, orgId: orgId, @@ -1074,20 +1092,19 @@ const loadAPIContentMd = async (req, res) => { let specHeading = 'OpenAPI Specification'; const apiType = metaData?.type; try { - if (apiType === constants.API_TYPE.GRAPHQL) { - specHeading = 'GraphQL Schema'; - const raw = await apiFileDao.get(constants.FILE_NAME.API_DEFINITION_GRAPHQL, constants.DOC_TYPES.API_DEFINITION, orgId, apiId); - if (raw) apiDefinition = raw.file_content.toString(constants.CHARSET_UTF8); - } else if (apiType === constants.API_TYPE.MCP) { - specHeading = 'Tool Schema'; - const raw = await apiFileDao.getDoc(constants.DOC_TYPES.SCHEMA_DEFINITION, orgId, apiId, null); - if (raw) apiDefinition = raw.file_content.toString(constants.CHARSET_UTF8); - } else { - if (apiType === constants.API_TYPE.WS || apiType === constants.API_TYPE.WEBSUB) specHeading = 'AsyncAPI Specification'; - else if (apiType === 'SOAP') specHeading = 'WSDL'; - const raw = await apiFileDao.get(constants.FILE_NAME.API_DEFINITION_FILE_NAME, constants.DOC_TYPES.API_DEFINITION, orgId, apiId); - if (raw) apiDefinition = raw.file_content.toString(constants.CHARSET_UTF8); - } + // Resolve the definition by its doc-type rather than a guessed filename: the stored + // name is the uploaded basename, so a fixed name would miss anything uploaded under a + // different name. There is one definition per API (SCHEMA_DEFINITION for MCP tools + // schemas, API_DEFINITION for every other type). + if (apiType === constants.API_TYPE.GRAPHQL) specHeading = 'GraphQL Schema'; + else if (apiType === constants.API_TYPE.MCP) specHeading = 'Tool Schema'; + else if (apiType === constants.API_TYPE.WS || apiType === constants.API_TYPE.WEBSUB) specHeading = 'AsyncAPI Specification'; + else if (apiType === 'SOAP') specHeading = 'WSDL'; + const definitionDocType = apiType === constants.API_TYPE.MCP + ? constants.DOC_TYPES.SCHEMA_DEFINITION + : constants.DOC_TYPES.API_DEFINITION; + const raw = await apiFileDao.getDoc(definitionDocType, orgId, apiId, null); + if (raw) apiDefinition = raw.file_content.toString(constants.CHARSET_UTF8); } catch (defErr) { logger.warn('Could not load API definition for markdown', { orgId, apiId, error: defErr.message }); } @@ -1380,13 +1397,13 @@ const loadAPIDefinitionRaw = async (req, res) => { const orgId = orgDetails.uuid; if (await isAiDisabledForPortal(orgId, viewName)) { - return res.status(404).json({ message: 'Not Found' }); + return util.sendError(res, 404, 'Not Found'); } const definitionResponse = await getAPIDefinition(orgName, viewName, apiHandle); if (definitionResponse.metaData?.agentVisibility === 'HIDDEN') { - return res.status(404).json({ message: 'API specification not found' }); + return util.sendError(res, 404, 'API specification not found'); } const typeConfig = SPEC_FORMAT_MAP[definitionResponse.apiType] || SPEC_FORMAT_DEFAULT; @@ -1396,7 +1413,7 @@ const loadAPIDefinitionRaw = async (req, res) => { } const raw = definitionResponse[typeConfig.field]; - if (!raw) return res.status(404).json({ message: 'API specification not found' }); + if (!raw) return util.sendError(res, 404, 'API specification not found'); const apiType = definitionResponse.apiType; @@ -1434,7 +1451,7 @@ const loadAPIDefinitionRaw = async (req, res) => { error: error.message, stack: error.stack }); - res.status(500).json({ message: 'Failed to load specification.' }); + util.sendError(res, 500, 'Failed to load specification.'); } }; @@ -1484,7 +1501,7 @@ const seedSamples = async (req, res) => { // anyone (including anonymous visitors) populate a public demo instance with samples. // Outside demo mode this is unreachable regardless of login/role. if (!config.demo?.enabled) { - return res.status(403).json({ error: 'Demo mode disabled' }); + return util.sendError(res, 403, 'Demo mode disabled'); } try { const orgDetails = await orgDao.get(orgName); @@ -1499,7 +1516,7 @@ const seedSamples = async (req, res) => { res.json({ results, deployed, skipped, failed }); } catch (err) { logger.error('Sample seed error', { orgName, error: err.message }); - res.status(500).json({ error: 'Failed to seed samples' }); + util.sendError(res, 500, 'Failed to seed samples'); } }; diff --git a/portals/developer-portal/src/controllers/apiKeyController.js b/portals/developer-portal/src/controllers/apiKeyController.js index 9b555aec07..69dd891212 100644 --- a/portals/developer-portal/src/controllers/apiKeyController.js +++ b/portals/developer-portal/src/controllers/apiKeyController.js @@ -55,7 +55,7 @@ async function resolveApiId(orgId, apiHandle, req) { async function resolveApiIdOrRespond(orgId, apiHandle, res, req) { const apiId = await resolveApiId(orgId, apiHandle, req); if (!apiId) { - res.status(404).json({ code: '404', message: 'Not Found', description: 'API not found' }); + util.sendError(res, 404, 'Not Found', { errors: [{ message: 'API not found' }] }); return null; } return apiId; @@ -76,7 +76,7 @@ async function resolveKeyId(orgId, apiId, keyHandle) { async function resolveKeyIdOrRespond(orgId, apiId, keyHandle, res) { const keyId = await resolveKeyId(orgId, apiId, keyHandle); if (!keyId) { - res.status(404).json({ code: '404', message: 'Not Found', description: 'API key not found' }); + util.sendError(res, 404, 'Not Found', { errors: [{ message: 'API key not found' }] }); return null; } return keyId; @@ -115,7 +115,7 @@ async function generateApiKey(req, res) { const appIdResult = normalizeOptionalId(appHandle); if (!appIdResult.ok) { - return res.status(400).json({ code: '400', message: 'Bad Request', description: 'appId must be a non-empty string' }); + return util.sendError(res, 400, 'Bad Request', { errors: [{ message: 'appId must be a non-empty string' }] }); } try { @@ -123,7 +123,7 @@ async function generateApiKey(req, res) { if (!apiId) return; const appId = await resolveAppId(orgId, util.resolveActor(req), appIdResult.value); if (appIdResult.value && !appId) { - return res.status(404).json({ code: '404', message: 'Not Found', description: 'Application not found' }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Application not found' }] }); } const result = await apiKeyService.generate({ orgId, apiId, subscriptionId, appId, handle: id, displayName, expiresAt, @@ -133,7 +133,7 @@ async function generateApiKey(req, res) { return res.status(201).json(result); } catch (err) { logger.error('Failed to generate API key', { error: err.message, orgId, apiId: apiHandle }); - return res.status(errorStatus(err)).json({ code: String(errorStatus(err)), message: 'Failed to generate API key' }); + return util.sendError(res, errorStatus(err), 'Failed to generate API key'); } } @@ -164,13 +164,13 @@ async function listApiKeys(req, res) { const apiId = await resolveApiId(orgId, apiHandle, req); if (!apiId) { return res.status(404).json({ - status: 'error', code: '404', message: 'Not Found', errors: [{ field: 'apiId', message: 'API not found' }], + status: 'error', code: 'RESOURCE_NOT_FOUND', message: 'Not Found', errors: [{ field: 'apiId', message: 'API not found' }], }); } const appId = await resolveAppId(orgId, util.resolveActor(req), appIdResult.value); if (appIdResult.value && !appId) { return res.status(404).json({ - status: 'error', code: '404', message: 'Not Found', errors: [{ field: 'appId', message: 'Application not found' }], + status: 'error', code: 'RESOURCE_NOT_FOUND', message: 'Not Found', errors: [{ field: 'appId', message: 'Application not found' }], }); } const keys = await apiKeyService.list(orgId, { @@ -203,7 +203,7 @@ async function regenerateApiKey(req, res) { const { keyId: keyHandle, expiresAt } = req.body || {}; if (!keyHandle || typeof keyHandle !== 'string' || !keyHandle.trim()) { - return res.status(400).json({ code: '400', message: 'Bad Request', description: 'keyId is required' }); + return util.sendError(res, 400, 'Bad Request', { errors: [{ message: 'keyId is required' }] }); } try { @@ -218,7 +218,7 @@ async function regenerateApiKey(req, res) { return res.status(200).json(result); } catch (err) { logger.error('Failed to regenerate API key', { error: err.message, orgId, apiId: apiHandle, keyHandle }); - return res.status(errorStatus(err)).json({ code: String(errorStatus(err)), message: 'Failed to regenerate API key' }); + return util.sendError(res, errorStatus(err), 'Failed to regenerate API key'); } } @@ -232,7 +232,7 @@ async function revokeApiKey(req, res) { const { keyId: keyHandle } = req.body || {}; if (!keyHandle || typeof keyHandle !== 'string' || !keyHandle.trim()) { - return res.status(400).json({ code: '400', message: 'Bad Request', description: 'keyId is required' }); + return util.sendError(res, 400, 'Bad Request', { errors: [{ message: 'keyId is required' }] }); } try { @@ -245,7 +245,7 @@ async function revokeApiKey(req, res) { return res.status(204).send(); } catch (err) { logger.error('Failed to revoke API key', { error: err.message, orgId, apiId: apiHandle, keyHandle }); - return res.status(errorStatus(err)).json({ code: String(errorStatus(err)), message: 'Failed to revoke API key' }); + return util.sendError(res, errorStatus(err), 'Failed to revoke API key'); } } @@ -259,10 +259,10 @@ async function associateApiKeyApplication(req, res) { const { keyId: keyHandle, appId: appHandle } = req.body || {}; if (!keyHandle || typeof keyHandle !== 'string' || !keyHandle.trim()) { - return res.status(400).json({ code: '400', message: 'Bad Request', description: 'keyId is required' }); + return util.sendError(res, 400, 'Bad Request', { errors: [{ message: 'keyId is required' }] }); } if (!appHandle || typeof appHandle !== 'string' || !appHandle.trim()) { - return res.status(400).json({ code: '400', message: 'Bad Request', description: 'appId is required' }); + return util.sendError(res, 400, 'Bad Request', { errors: [{ message: 'appId is required' }] }); } try { @@ -272,7 +272,7 @@ async function associateApiKeyApplication(req, res) { if (!keyId) return; const appId = await resolveAppId(orgId, util.resolveActor(req), appHandle.trim()); if (!appId) { - return res.status(404).json({ code: '404', message: 'Not Found', description: 'Application not found' }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Application not found' }] }); } const result = await apiKeyService.associateApplication({ orgId, apiId, keyId, appId, actor: util.resolveActor(req), @@ -281,7 +281,7 @@ async function associateApiKeyApplication(req, res) { return res.status(200).json(result); } catch (err) { logger.error('Failed to associate application with API key', { error: err.message, orgId, apiId: apiHandle, keyHandle }); - return res.status(errorStatus(err)).json({ code: String(errorStatus(err)), message: 'Failed to associate application with API key' }); + return util.sendError(res, errorStatus(err), 'Failed to associate application with API key'); } } @@ -295,7 +295,7 @@ async function removeApiKeyApplication(req, res) { const { keyId: keyHandle } = req.body || {}; if (!keyHandle || typeof keyHandle !== 'string' || !keyHandle.trim()) { - return res.status(400).json({ code: '400', message: 'Bad Request', description: 'keyId is required' }); + return util.sendError(res, 400, 'Bad Request', { errors: [{ message: 'keyId is required' }] }); } try { @@ -308,7 +308,7 @@ async function removeApiKeyApplication(req, res) { return res.status(204).send(); } catch (err) { logger.error('Failed to remove application association from API key', { error: err.message, orgId, apiId: apiHandle, keyHandle }); - return res.status(errorStatus(err)).json({ code: String(errorStatus(err)), message: 'Failed to remove application association from API key' }); + return util.sendError(res, errorStatus(err), 'Failed to remove application association from API key'); } } @@ -323,7 +323,7 @@ async function listApplicationApiKeys(req, res) { try { const appRecord = await applicationDao.getId(orgId, util.resolveActor(req), applicationHandle); if (!appRecord) { - return res.status(404).json({ code: '404', message: 'Application not found' }); + return util.sendError(res, 404, 'Application not found'); } const applicationId = appRecord.uuid; const keys = await apiKeyService.list(orgId, { appId: applicationId }); diff --git a/portals/developer-portal/src/controllers/apiWorkflowsController.js b/portals/developer-portal/src/controllers/apiWorkflowsController.js index 9a1484c881..18af720208 100644 --- a/portals/developer-portal/src/controllers/apiWorkflowsController.js +++ b/portals/developer-portal/src/controllers/apiWorkflowsController.js @@ -234,13 +234,13 @@ const getFlowPromptJSON = async (req, res) => { try { const orgDetails = await orgDao.get(orgName); if (!orgDetails) { - return res.status(404).json({ error: 'Organization not found' }); + return util.sendError(res, 404, 'Organization not found'); } const orgId = orgDetails.uuid; if (await isAiDisabledForPortal(orgId, viewName)) { - return res.status(404).json({ error: 'Not Found' }); + return util.sendError(res, 404, 'Not Found'); } const viewId = await resolveViewId(orgId, viewName); @@ -248,7 +248,7 @@ const getFlowPromptJSON = async (req, res) => { const apiWorkflow = await apiWorkflowDao.getPublishedByHandle(orgId, viewId, handle, { agentVisibility: 'VISIBLE' }); if (!apiWorkflow) { - return res.status(404).json({ error: 'API Workflow not found or not published' }); + return util.sendError(res, 404, 'API Workflow not found or not published'); } const rawContent = apiWorkflow.file_content; @@ -275,7 +275,7 @@ const getFlowPromptJSON = async (req, res) => { viewName, handle }); - res.status(500).json({ error: 'Error fetching API workflow' }); + util.sendError(res, 500, 'Error fetching API workflow'); } }; @@ -431,7 +431,7 @@ const generatePrompt = async (req, res) => { res.status(200).json({ agentPrompt: prompt }); } catch (error) { logger.error('Error generating agent prompt', { error: error.message }); - res.status(500).json({ message: 'Error generating agent prompt' }); + util.sendError(res, 500, 'Error generating agent prompt'); } }; @@ -441,28 +441,28 @@ const getWorkflowArazzoSpec = async (req, res) => { try { const orgDetails = await orgDao.get(orgName); if (!orgDetails) { - return res.status(404).json({ error: 'Organization not found' }); + return util.sendError(res, 404, 'Organization not found'); } const orgId = orgDetails.uuid; if (await isAiDisabledForPortal(orgId, viewName)) { - return res.status(404).json({ error: 'Not Found' }); + return util.sendError(res, 404, 'Not Found'); } const viewId = await resolveViewId(orgId, viewName); const apiWorkflow = await apiWorkflowDao.getPublishedByHandle(orgId, viewId, handle); if (!apiWorkflow) { - return res.status(404).json({ error: 'API Workflow not found or not published' }); + return util.sendError(res, 404, 'API Workflow not found or not published'); } if ((apiWorkflow.agent_visibility || constants.AGENT_VISIBILITY.VISIBLE) === constants.AGENT_VISIBILITY.HIDDEN) { - return res.status(404).json({ error: 'API Workflow not found or not published' }); + return util.sendError(res, 404, 'API Workflow not found or not published'); } if (apiWorkflow.content_type !== 'ARAZZO') { - return res.status(404).json({ error: 'This workflow does not have an Arazzo specification' }); + return util.sendError(res, 404, 'This workflow does not have an Arazzo specification'); } const rawContent = apiWorkflow.file_content; @@ -478,7 +478,7 @@ const getWorkflowArazzoSpec = async (req, res) => { viewName, handle }); - res.status(500).json({ error: 'Error fetching Arazzo specification' }); + util.sendError(res, 500, 'Error fetching Arazzo specification'); } }; diff --git a/portals/developer-portal/src/controllers/devportalController.js b/portals/developer-portal/src/controllers/devportalController.js index 2d8f1d77ad..32e9aa7d58 100644 --- a/portals/developer-portal/src/controllers/devportalController.js +++ b/portals/developer-portal/src/controllers/devportalController.js @@ -267,7 +267,7 @@ const generateKeys = async (req, res) => { const appHandle = req.params.applicationId; const appRecord = await appDao.getId(orgId, userId, appHandle); if (!appRecord) { - return res.status(404).json({ message: 'Application not found' }); + return util.sendError(res, 404, 'Application not found'); } appId = appRecord.uuid; logger.info('Initiate create application key mapping...', { orgId: orgId, appId: appId }); @@ -278,17 +278,17 @@ const generateKeys = async (req, res) => { } = req.body; if (!consumerKey) { - return res.status(400).json({ message: 'consumerKey is required.' }); + return util.sendError(res, 400, 'consumerKey is required.'); } const kmRecord = await kmDao.getByHandle(orgId, kmName); if (!kmRecord) { - return res.status(404).json({ message: `Key manager '${kmName}' not found.` }); + return util.sendError(res, 404, `Key manager '${kmName}' not found.`); } const keyType = (rawKeyType || constants.KEY_TYPE.PRODUCTION).toUpperCase(); if (!Object.values(constants.KEY_TYPE).includes(keyType)) { - return res.status(400).json({ message: `Invalid type. Must be one of: ${Object.values(constants.KEY_TYPE).join(', ')}.` }); + return util.sendError(res, 400, `Invalid type. Must be one of: ${Object.values(constants.KEY_TYPE).join(', ')}.`); } const appKeyMapping = { diff --git a/portals/developer-portal/src/controllers/viewConfigureController.js b/portals/developer-portal/src/controllers/viewConfigureController.js index 1c42fc95ed..35b87a31f0 100644 --- a/portals/developer-portal/src/controllers/viewConfigureController.js +++ b/portals/developer-portal/src/controllers/viewConfigureController.js @@ -220,7 +220,7 @@ const getLlmsConfig = async (req, res) => { res.json(JSON.parse(asset.file_content.toString('utf8'))); } catch (err) { logger.error('Error getting llms config', { error: err.message, stack: err.stack }); - res.status(500).json({ error: 'Failed to get LLMs configuration' }); + util.sendError(res, 500, 'Failed to get LLMs configuration'); } }; @@ -253,7 +253,7 @@ const saveLlmsConfig = async (req, res) => { res.json({ message: 'Saved successfully' }); } catch (err) { logger.error('Error saving llms config', { error: err.message, stack: err.stack }); - res.status(500).json({ error: 'Failed to save LLMs configuration' }); + util.sendError(res, 500, 'Failed to save LLMs configuration'); } }; diff --git a/portals/developer-portal/src/controllers/webhookAdminController.js b/portals/developer-portal/src/controllers/webhookAdminController.js index d751272475..eeb15494e0 100644 --- a/portals/developer-portal/src/controllers/webhookAdminController.js +++ b/portals/developer-portal/src/controllers/webhookAdminController.js @@ -17,6 +17,7 @@ */ const eventDao = require('../dao/eventDao'); const logger = require('../config/logger'); +const util = require('../utils/util'); function formatDelivery(d) { return { @@ -62,12 +63,13 @@ async function listEvents(req, res) { offset: parsedOffset, }); res.json({ + count: result.rows.length, list: result.rows.map(formatEvent), - pagination: { total: result.count, limit: parsedLimit, offset: parsedOffset }, + pagination: { limit: parsedLimit, offset: parsedOffset, total: result.count }, }); } catch (err) { logger.error('Failed to list events', { error: err.message }); - res.status(500).json({ message: 'Failed to list events' }); + util.sendError(res, 500, 'Failed to list events'); } } @@ -78,12 +80,12 @@ async function getEvent(req, res) { try { const event = await eventDao.get(req.params.eventId); if (!event || event.org_uuid !== req.orgId) { - return res.status(404).json({ message: 'Event not found' }); + return util.sendError(res, 404, 'Event not found'); } res.json(formatEvent(event)); } catch (err) { logger.error('Failed to get event', { error: err.message }); - res.status(500).json({ message: 'Failed to get event' }); + util.sendError(res, 500, 'Failed to get event'); } } diff --git a/portals/developer-portal/src/defaultContent/pages/api-keys/page.hbs b/portals/developer-portal/src/defaultContent/pages/api-keys/page.hbs index 2f84f9eecb..c36203849a 100644 --- a/portals/developer-portal/src/defaultContent/pages/api-keys/page.hbs +++ b/portals/developer-portal/src/defaultContent/pages/api-keys/page.hbs @@ -37,7 +37,7 @@ @@ -45,7 +45,7 @@
diff --git a/portals/developer-portal/src/defaultContent/pages/api-landing/partials/api-detail-banner.hbs b/portals/developer-portal/src/defaultContent/pages/api-landing/partials/api-detail-banner.hbs index 9c36d077d9..763b70da8a 100644 --- a/portals/developer-portal/src/defaultContent/pages/api-landing/partials/api-detail-banner.hbs +++ b/portals/developer-portal/src/defaultContent/pages/api-landing/partials/api-detail-banner.hbs @@ -62,9 +62,11 @@ Documentation {{else}} + {{#if schemaUrl}} Download + {{/if}} {{/in}} {{#if showApiWorkflowsNav}} {{#unless (eq apiMetadata.agentVisibility "HIDDEN")}} diff --git a/portals/developer-portal/src/defaultContent/pages/docs/partials/api-doc.hbs b/portals/developer-portal/src/defaultContent/pages/docs/partials/api-doc.hbs index 4154822b45..92bd05a2ce 100644 --- a/portals/developer-portal/src/defaultContent/pages/docs/partials/api-doc.hbs +++ b/portals/developer-portal/src/defaultContent/pages/docs/partials/api-doc.hbs @@ -20,7 +20,7 @@
{{#if (eq apiType "GRAPHQL")}}schema.graphql - {{else if (eq apiType "SOAP")}}apiDefinition.xml + {{else if (eq apiType "SOAP")}}definition.xml {{else if (eq apiType "WS")}}asyncapi.yaml {{else if (eq apiType "WebSubApi")}}asyncapi.yaml {{else}}openapi.yaml diff --git a/portals/developer-portal/src/defaultContent/pages/mcp-landing/page.hbs b/portals/developer-portal/src/defaultContent/pages/mcp-landing/page.hbs index af9664e85a..51d55dde9c 100644 --- a/portals/developer-portal/src/defaultContent/pages/mcp-landing/page.hbs +++ b/portals/developer-portal/src/defaultContent/pages/mcp-landing/page.hbs @@ -35,7 +35,7 @@
diff --git a/portals/developer-portal/src/defaultContent/pages/mcp-landing/partials/mcp-detail-banner.hbs b/portals/developer-portal/src/defaultContent/pages/mcp-landing/partials/mcp-detail-banner.hbs index 4fe9790b2d..452da6cf2e 100644 --- a/portals/developer-portal/src/defaultContent/pages/mcp-landing/partials/mcp-detail-banner.hbs +++ b/portals/developer-portal/src/defaultContent/pages/mcp-landing/partials/mcp-detail-banner.hbs @@ -4,11 +4,11 @@ {{#if apiMetadata.apiImageMetadata.api-icon}} {{apiMetadata.name}} {{else}} -
{{firstTwoLetters (or apiMetadata.apiTitle apiMetadata.name)}}
+
{{firstTwoLetters (or apiMetadata.title apiMetadata.name)}}
{{/if}}
-

{{or apiMetadata.apiTitle apiMetadata.name}}

+

{{or apiMetadata.title apiMetadata.name}}

{{#if apiMetadata.version}} {{apiMetadata.version}} diff --git a/portals/developer-portal/src/defaultContent/pages/mcp/partials/mcp-listing.hbs b/portals/developer-portal/src/defaultContent/pages/mcp/partials/mcp-listing.hbs index 036309d778..8f1a115791 100644 --- a/portals/developer-portal/src/defaultContent/pages/mcp/partials/mcp-listing.hbs +++ b/portals/developer-portal/src/defaultContent/pages/mcp/partials/mcp-listing.hbs @@ -73,12 +73,12 @@
{{#if apiImageMetadata.api-icon}} - {{or apiTitle name}} + {{or title name}} {{else}} - {{firstTwoLetters (or apiTitle name)}} + {{firstTwoLetters (or title name)}} {{/if}}
-
{{or apiTitle name}}
+
{{or title name}}
{{version}} MCP diff --git a/portals/developer-portal/src/dto/apiDto.js b/portals/developer-portal/src/dto/apiDto.js index 22e9805ac3..a022f8e3d4 100644 --- a/portals/developer-portal/src/dto/apiDto.js +++ b/portals/developer-portal/src/dto/apiDto.js @@ -46,7 +46,7 @@ class APIDTO { class APIInfo { constructor(apiInfo) { this.name = apiInfo.name; - this.apiTitle = apiInfo.metadata_search?.apiTitle || null; + this.title = apiInfo.metadata_search?.title || null; this.remotes = apiInfo.metadata_search?.remotes || []; this.version = apiInfo.version; this.description = apiInfo.description; diff --git a/portals/developer-portal/src/dto/mcpServerDto.js b/portals/developer-portal/src/dto/mcpServerDto.js index 73d1b22347..049de8f074 100644 --- a/portals/developer-portal/src/dto/mcpServerDto.js +++ b/portals/developer-portal/src/dto/mcpServerDto.js @@ -42,7 +42,7 @@ class ServerResponseDTO { this.server = { $schema: 'https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json', name: source.metadata_search?.proxyId || source.name, - title: source.metadata_search?.apiTitle || undefined, + title: source.metadata_search?.title || undefined, version: source.version, description: source.description, remotes diff --git a/portals/developer-portal/src/pages/settings/page.hbs b/portals/developer-portal/src/pages/settings/page.hbs index 3938c019cd..a9ebefdf4e 100644 --- a/portals/developer-portal/src/pages/settings/page.hbs +++ b/portals/developer-portal/src/pages/settings/page.hbs @@ -1604,7 +1604,7 @@ if (specStepLabel) specStepLabel.textContent = isMcp ? 'Tools Schema' : 'Spec'; var specDesc = document.querySelector('#cfg-wstep-1 .cfg-wstep-desc'); if (specDesc) specDesc.innerHTML = isMcp - ? 'Upload the MCP tools schema that defines this server’s tools. * Required. Accepted: YAML or JSON (e.g. schemaDefinition.yaml).' + ? 'Upload the MCP tools schema that defines this server’s tools. * Required. Accepted: YAML or JSON (e.g. definition.yaml).' : 'Upload the contract that defines this API. * Required. Accepted: OpenAPI (.json / .yaml), AsyncAPI, GraphQL SDL (.graphql), or WSDL (.wsdl / .xml).'; var specOnFileTxt = document.querySelector('#wz-spec-onfile span'); if (specOnFileTxt) specOnFileTxt.textContent = isMcp @@ -1749,10 +1749,10 @@ }; var fd = new FormData(); - fd.append('apiMetadata', JSON.stringify(meta)); - /* For MCP servers the uploaded contract is the tools schema (stored as SCHEMA_DEFINITION), - so it goes in the schemaDefinition field; every other type uses apiDefinition. */ - if (specFile) fd.append(meta.type === 'Mcp' ? 'schemaDefinition' : 'apiDefinition', specFile); + fd.append('metadata', JSON.stringify(meta)); + /* The single `definition` field carries the contract for every type. For MCP servers it is + the tools schema (stored as SCHEMA_DEFINITION); for other types it is the API definition. */ + if (specFile) fd.append('definition', specFile); docFiles.forEach(function(f) { fd.append('docs', f); }); /* MCP servers are created/updated via /mcp-servers (which requires type MCP and rejects @@ -1921,7 +1921,7 @@ subscriptionPlans: apiData.subscriptionPlans || [], }); var fd = new FormData(); - fd.append('apiMetadata', JSON.stringify(meta)); + fd.append('metadata', JSON.stringify(meta)); var putRes = await fetch(window.devportalApi.root(base+'/'+encodeURIComponent(apiId)), { method: 'PUT', headers: { 'X-CSRF-Token': window.devportalApi.csrfToken() }, @@ -2304,7 +2304,7 @@ } try { var fd = new FormData(); - fd.append('apiContent', contentZipFile); + fd.append('content', contentZipFile); var res = await fetch(window.devportalApi.root('/apis/' + encodeURIComponent(editingId) + '/assets'), { method: 'PUT', headers: { 'X-CSRF-Token': window.devportalApi.csrfToken() }, diff --git a/portals/developer-portal/src/services/adminService.js b/portals/developer-portal/src/services/adminService.js index 1fd9c7656e..be2293c6da 100644 --- a/portals/developer-portal/src/services/adminService.js +++ b/portals/developer-portal/src/services/adminService.js @@ -278,7 +278,7 @@ const updateOrganization = async (req, res) => { const devportalMode = payload.configuration?.devportalMode; if (devportalMode !== undefined && !Object.values(constants.DEVPORTAL_MODE).includes(devportalMode)) { - return res.status(400).json({ error: `Invalid devportalMode '${devportalMode}'. Must be one of: ${Object.values(constants.DEVPORTAL_MODE).join(', ')}.` }); + return util.sendError(res, 400, `Invalid devportalMode '${devportalMode}'. Must be one of: ${Object.values(constants.DEVPORTAL_MODE).join(', ')}.`); } let updatedOrg; diff --git a/portals/developer-portal/src/services/apiMetadataService.js b/portals/developer-portal/src/services/apiMetadataService.js index 3077a74360..5a23503a2c 100644 --- a/portals/developer-portal/src/services/apiMetadataService.js +++ b/portals/developer-portal/src/services/apiMetadataService.js @@ -53,6 +53,8 @@ const createAPIMetadata = async (req, res) => { let apiDefinitionFile, apiFileName = ""; let fullApiBundle; const apiArtifactFile = req.files?.artifact?.[0]; + // Single multipart field carrying the contract on a non-artifact upload; interpreted by type below. + const definitionUpload = req.files?.definition?.[0] || null; try { let artifactApiContent = []; @@ -77,42 +79,41 @@ const createAPIMetadata = async (req, res) => { file.key = filenameToKey[file.fileName]; } }); - } else if (req.files?.api?.[0]) { + } else if (req.files?.metadata?.[0]) { + // `metadata` supplied as an uploaded YAML/JSON file (k8s-style artifact document). apiMetadata = parseApiMetadataFromYamlRequest(req); - if (req.files?.apiDefinition?.[0]) { - const file = req.files.apiDefinition[0]; - const preparedDefinition = prepareApiDefinitionForStorage(file.originalname, file.buffer); - apiDefinitionFile = preparedDefinition.apiDefinitionFile; - apiFileName = preparedDefinition.apiDefinitionFileName; - } } else { - apiMetadata = JSON.parse(req.body.apiMetadata); + if (!req.body.metadata) { + throw new Sequelize.ValidationError("Missing or invalid fields in the request payload"); + } + apiMetadata = JSON.parse(req.body.metadata); // Type is resolved centrally below, using this raw (possibly omitted) value — // don't resolve here, or an omitted type on a /mcp-servers request can no // longer be told apart from an explicit REST. if (apiMetadata.id) { apiMetadata.handle = apiMetadata.id; } - if (req.files?.apiDefinition?.[0]) { - const file = req.files.apiDefinition[0]; - const preparedDefinition = prepareApiDefinitionForStorage(file.originalname, file.buffer); - apiDefinitionFile = preparedDefinition.apiDefinitionFile; - apiFileName = preparedDefinition.apiDefinitionFileName; - } } apiMetadata.type = resolveTypeOrReject(apiMetadata.type, req.__forceApiType); - // Validate input. An MCP server's contract IS its tools schema (schemaDefinition) — - // it has no OpenAPI-style apiDefinition, so it requires a schemaDefinition and any - // apiDefinition sent alongside is ignored (mirrors how sampleSeeder creates MCP servers - // from api.yaml + schemaDefinition.yaml with no definition file). GraphQL may carry its - // SDL as a schemaDefinition; every other type requires an apiDefinition. + // Validate input. A single `definition` multipart field carries the contract for a + // non-artifact upload. Its meaning depends on the resolved type: for REST/SOAP/etc. it + // is the OpenAPI/WSDL definition (prepared and stored as an apiDefinition here); for + // GraphQL it is the SDL and for MCP it is the tools schema — both stored by their + // type-specific branches below. An MCP server's contract IS its tools schema; it has no + // OpenAPI-style apiDefinition (mirrors how sampleSeeder creates MCP servers from api.yaml + // plus a definition file). const isMcp = apiMetadata.type === constants.API_TYPE.MCP; - const hasGraphQLSchema = apiMetadata.type === constants.API_TYPE.GRAPHQL && - req.files?.schemaDefinition?.[0]; + const isGraphQL = apiMetadata.type === constants.API_TYPE.GRAPHQL; + if (definitionUpload && !fullApiBundle && !isMcp && !isGraphQL) { + const preparedDefinition = prepareApiDefinitionForStorage(definitionUpload.originalname, definitionUpload.buffer); + apiDefinitionFile = preparedDefinition.apiDefinitionFile; + apiFileName = preparedDefinition.apiDefinitionFileName; + } + const hasGraphQLSchema = isGraphQL && (definitionUpload || fullApiBundle?.apiDefinitionFile); const hasMcpSchema = isMcp && - (req.files?.schemaDefinition?.[0] || fullApiBundle?.schemaDefinitionFile); + (definitionUpload || fullApiBundle?.schemaDefinitionFile); const hasContract = isMcp ? hasMcpSchema : (apiDefinitionFile || hasGraphQLSchema); if (!apiMetadata.name || !hasContract || !apiMetadata.endPoints) { throw new Sequelize.ValidationError( @@ -183,8 +184,8 @@ const createAPIMetadata = async (req, res) => { } await tagDao.createApiMapping(orgId, apiId, tags, userId, t); } - // store api definition file (skipped for GraphQL — schema stored below via schemaDefinition; - // and for MCP, whose contract is its schemaDefinition — an MCP has no apiDefinition) + // store api definition file (skipped for GraphQL — schema stored below via the definition + // field; and for MCP, whose contract is its tools schema — an MCP has no apiDefinition) if (apiDefinitionFile && !isMcp) { await apiFileDao.store(apiDefinitionFile, apiFileName, apiId, constants.DOC_TYPES.API_DEFINITION, userId, t); } @@ -197,8 +198,8 @@ const createAPIMetadata = async (req, res) => { // Save MCP tools as schema definition if the API type is MCP if (constants.API_TYPE.MCP === apiMetadata.type) { let schemaFile; - if (req.files?.schemaDefinition?.[0]) { - schemaFile = req.files.schemaDefinition[0]; + if (definitionUpload) { + schemaFile = definitionUpload; } else if (fullApiBundle?.schemaDefinitionFile) { schemaFile = { originalname: fullApiBundle.schemaDefinitionFileName, @@ -221,8 +222,8 @@ const createAPIMetadata = async (req, res) => { } } - if (constants.API_TYPE.GRAPHQL === apiMetadata.type && req.files?.schemaDefinition?.[0]) { - const file = req.files.schemaDefinition[0]; + if (constants.API_TYPE.GRAPHQL === apiMetadata.type && definitionUpload) { + const file = definitionUpload; const schemaDefinitionFile = file.buffer; logger.debug('GraphQL schema definition file received', { apiId: apiId, @@ -370,7 +371,7 @@ const getAPIMetadata = async (req, res) => { // Create response object res.status(200).send(retrievedAPI); } else { - res.status(404).send("API not found"); + util.sendError(res, 404, 'API not found'); } } catch (error) { logger.error('API metadata retrieval failed', { @@ -402,7 +403,7 @@ const getAllAPIMetadata = async (req, res) => { try { const orgId = req.orgId; const searchTerm = req.query.query; - const apiName = req.query.apiName; + const apiName = req.query.name; const apiVersion = req.query.version; const tags = req.query.tags; const view = req.query.view; @@ -414,7 +415,7 @@ const getAllAPIMetadata = async (req, res) => { stack: error.stack, orgId: req.orgId, searchTerm: req.query.query, - apiName: req.query.apiName, + apiName: req.query.name, apiVersion: req.query.version, tags: req.query.tags, view: req.query.view @@ -464,11 +465,13 @@ const updateAPIMetadata = async (req, res) => { let apiDefinitionFile, apiFileName = ""; let fullApiBundle; const apiArtifactFile = req.files?.artifact?.[0]; + // Single multipart field carrying the contract on a non-artifact upload; interpreted by type below. + const definitionUpload = req.files?.definition?.[0] || null; try { apiId = await resolveScopedApiId(req, orgId, apiHandle); if (!apiId) { - return res.status(404).send("API not found"); + return util.sendError(res, 404, 'API not found'); } // `type` is immutable after creation — resolveTypeOrReject below only enforces the // /apis-vs-/mcp-servers family boundary, not that the value matches this specific @@ -503,28 +506,20 @@ const updateAPIMetadata = async (req, res) => { file.key = filenameToKey[file.fileName]; } }); - } else if (req.files?.api?.[0]) { + } else if (req.files?.metadata?.[0]) { + // `metadata` supplied as an uploaded YAML/JSON file (k8s-style artifact document). apiMetadata = parseApiMetadataFromYamlRequest(req); - if (req.files?.apiDefinition?.[0]) { - const file = req.files.apiDefinition[0]; - const preparedDefinition = prepareApiDefinitionForStorage(file.originalname, file.buffer); - apiDefinitionFile = preparedDefinition.apiDefinitionFile; - apiFileName = preparedDefinition.apiDefinitionFileName; - } } else { - apiMetadata = JSON.parse(req.body.apiMetadata); + if (!req.body.metadata) { + throw new Sequelize.ValidationError("Missing or invalid fields in the request payload"); + } + apiMetadata = JSON.parse(req.body.metadata); // Type is resolved centrally below, using this raw (possibly omitted) value — // don't resolve here, or an omitted type on a /mcp-servers request can no // longer be told apart from an explicit REST. if (apiMetadata.id) { apiMetadata.handle = apiMetadata.id; } - if (req.files?.apiDefinition?.[0]) { - const file = req.files.apiDefinition[0]; - const preparedDefinition = prepareApiDefinitionForStorage(file.originalname, file.buffer); - apiDefinitionFile = preparedDefinition.apiDefinitionFile; - apiFileName = preparedDefinition.apiDefinitionFileName; - } } apiMetadata.type = resolveTypeOrReject(apiMetadata.type, req.__forceApiType); @@ -532,7 +527,15 @@ const updateAPIMetadata = async (req, res) => { throw new CustomError(409, 'Conflict', `API type cannot be changed after creation (existing type is '${existingType}')`); } // An MCP server's contract is its tools schema (handled below); it has no apiDefinition. + // The single `definition` field is prepared as an apiDefinition only for the types that + // store one (not MCP, not GraphQL — those are handled by their branches below). const isMcp = apiMetadata.type === constants.API_TYPE.MCP; + const isGraphQL = apiMetadata.type === constants.API_TYPE.GRAPHQL; + if (definitionUpload && !fullApiBundle && !isMcp && !isGraphQL) { + const preparedDefinition = prepareApiDefinitionForStorage(definitionUpload.originalname, definitionUpload.buffer); + apiDefinitionFile = preparedDefinition.apiDefinitionFile; + apiFileName = preparedDefinition.apiDefinitionFileName; + } // Validate input — spec file is optional on update (already stored from create) if (!apiMetadata.name || !apiMetadata.endPoints) { @@ -661,7 +664,7 @@ const updateAPIMetadata = async (req, res) => { } } // Update MCP tools schema definition if the API type is MCP - const hasSchemaDefinitionFile = !!req.files?.schemaDefinition?.[0] || !!fullApiBundle?.schemaDefinitionFile; + const hasSchemaDefinitionFile = !!definitionUpload || !!fullApiBundle?.schemaDefinitionFile; logger.debug('Processing MCP API schema definition', { hasSchemaDefinition: hasSchemaDefinitionFile, apiType: apiMetadata.type, @@ -669,8 +672,8 @@ const updateAPIMetadata = async (req, res) => { }); if (constants.API_TYPE.MCP === apiMetadata.type && hasSchemaDefinitionFile) { let schemaFile; - if (req.files?.schemaDefinition?.[0]) { - schemaFile = req.files.schemaDefinition[0]; + if (definitionUpload) { + schemaFile = definitionUpload; } else if (fullApiBundle?.schemaDefinitionFile) { schemaFile = { originalname: fullApiBundle.schemaDefinitionFileName, @@ -693,8 +696,8 @@ const updateAPIMetadata = async (req, res) => { } } - if (constants.API_TYPE.GRAPHQL === apiMetadata.type && req.files?.schemaDefinition?.[0]) { - const file = req.files.schemaDefinition[0]; + if (constants.API_TYPE.GRAPHQL === apiMetadata.type && definitionUpload) { + const file = definitionUpload; const schemaDefinitionFile = file.buffer; const schemaFileName = constants.FILE_NAME.API_DEFINITION_GRAPHQL; logger.debug('GraphQL schema definition file received for update', { @@ -744,7 +747,7 @@ const deleteAPIMetadata = async (req, res) => { try { apiId = await resolveScopedApiId(req, orgId, apiHandle); if (!apiId) { - return res.status(404).send("API not found"); + return util.sendError(res, 404, 'API not found'); } const subApis = await subDao.listByApi(orgId, apiId); if (subApis.length > 0) { @@ -790,7 +793,7 @@ const createAPITemplate = async (req, res) => { const { apiId: apiHandle } = req.params; const apiId = await resolveScopedApiId(req, orgId, apiHandle); if (!apiId) { - return res.status(404).send("API not found"); + return util.sendError(res, 404, 'API not found'); } const userId = util.resolveActor(req); const zipFilePath = req.file.path; @@ -893,7 +896,7 @@ const createAPITemplate = async (req, res) => { }; const createAPIContent = async (req, res) => { - const uploadedFile = req.files?.apiContent?.[0] ?? req.file; + const uploadedFile = req.files?.content?.[0] ?? req.file; logger.info('Creating API content...', { orgId: req.orgId, apiId: req.params.apiId, @@ -904,7 +907,7 @@ const createAPIContent = async (req, res) => { const { apiId: apiHandle } = req.params; const apiId = await resolveScopedApiId(req, orgId, apiHandle); if (!apiId) { - return res.status(404).send("API not found"); + return util.sendError(res, 404, 'API not found'); } const userId = util.resolveActor(req); let apiContent = await extractApiContentFromUploadedZip(uploadedFile, orgId, apiId, 'classic'); @@ -963,7 +966,7 @@ const updateAPITemplate = async (req, res) => { const { apiId: apiHandle } = req.params; const apiId = await resolveScopedApiId(req, orgId, apiHandle); if (!apiId) { - return res.status(404).send("API not found"); + return util.sendError(res, 404, 'API not found'); } const userId = util.resolveActor(req); let imageMetadata; @@ -1061,7 +1064,7 @@ const updateAPITemplate = async (req, res) => { }; const updateAPIContent = async (req, res) => { - const uploadedFile = req.files?.apiContent?.[0] ?? req.file; + const uploadedFile = req.files?.content?.[0] ?? req.file; logger.info('Updating API content...', { orgId: req.orgId, apiId: req.params.apiId, @@ -1072,7 +1075,7 @@ const updateAPIContent = async (req, res) => { const { apiId: apiHandle } = req.params; const apiId = await resolveScopedApiId(req, orgId, apiHandle); if (!apiId) { - return res.status(404).send("API not found"); + return util.sendError(res, 404, 'API not found'); } const userId = util.resolveActor(req); let imageMetadata; @@ -1131,12 +1134,12 @@ const getAPIFile = async (req, res) => { try { apiId = await resolveScopedApiId(req, orgId, apiHandle); if (!apiId) { - return res.status(404).send("API not found"); + return util.sendError(res, 404, 'API not found'); } const fileExtension = path.extname(apiFileName).toLowerCase(); apiFileResponse = await apiFileDao.get(apiFileName, type, orgId, apiId); if (!apiFileResponse) { - return res.status(404).send("API File not found"); + return util.sendError(res, 404, 'API File not found'); } apiFile = apiFileResponse.file_content; //convert to text to check if link @@ -1196,7 +1199,7 @@ const deleteAPIFile = async (req, res) => { try { const apiId = await resolveScopedApiId(req, orgId, apiHandle); if (!apiId) { - return res.status(404).send("API not found"); + return util.sendError(res, 404, 'API not found'); } let apiFileResponse; if (apiFileName) { @@ -1207,7 +1210,7 @@ const deleteAPIFile = async (req, res) => { if (!apiFileResponse) { res.status(204).send(); } else { - res.status(404).send("API Content not found"); + util.sendError(res, 404, 'API Content not found'); } } catch (error) { logger.error('API content deletion failed', { @@ -1270,7 +1273,7 @@ const createSubscriptionPlan = async (req, res) => { }); if (!subscriptionPlan || typeof subscriptionPlan !== "object") { - return res.status(400).json({ message: "Request body is missing or invalid" }); + return util.sendError(res, 400, "Request body is missing or invalid"); } try { @@ -1312,7 +1315,7 @@ const createSubscriptionPlans = async (req, res) => { const userId = util.resolveActor(req); if (!Array.isArray(subscriptionPlans) || subscriptionPlans.length === 0) { - return res.status(400).json({ message: "Missing or invalid fields in the request payload" }); + return util.sendError(res, 400, "Missing or invalid fields in the request payload"); } const createdRecords = []; @@ -1359,7 +1362,7 @@ const updateSubscriptionPlan = async (req, res) => { const userId = util.resolveActor(req); if (!subscriptionPlan || typeof subscriptionPlan !== "object") { - return res.status(400).json({ message: "Request body is missing or invalid" }); + return util.sendError(res, 400, "Request body is missing or invalid"); } try { @@ -1398,7 +1401,7 @@ const updateSubscriptionPlans = async (req, res) => { const userId = util.resolveActor(req); if (!Array.isArray(subscriptionPlans) || subscriptionPlans.length === 0) { - return res.status(400).json({ message: "Missing or invalid fields in the request payload" }); + return util.sendError(res, 400, "Missing or invalid fields in the request payload"); } const updatedRecords = []; @@ -1539,7 +1542,7 @@ const getLabel = async (req, res) => { try { const labelId = await labelDao.getIdByHandle(orgId, labelHandle); if (!labelId) { - return res.status(404).json({ code: '404', message: 'Not Found', description: 'Label not found' }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Label not found' }] }); } const record = await labelDao.findById(orgId, labelId); res.status(200).json(new LabelDTO(record)); @@ -1558,7 +1561,7 @@ const updateLabel = async (req, res) => { try { const labelId = await labelDao.getIdByHandle(orgId, labelHandle); if (!labelId) { - return res.status(404).json({ code: '404', message: 'Not Found', description: 'Label not found' }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Label not found' }] }); } const record = await labelDao.updateById(orgId, labelId, label, userId); res.status(200).json(new LabelDTO(record)); @@ -1575,7 +1578,7 @@ const deleteLabel = async (req, res) => { try { const labelId = await labelDao.getIdByHandle(orgId, labelHandle); if (!labelId) { - return res.status(404).json({ code: '404', message: 'Not Found', description: 'Label not found' }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Label not found' }] }); } await labelDao.deleteById(orgId, labelId); res.status(204).send(); @@ -1715,7 +1718,7 @@ const getView = async (req, res) => { if (view) { res.status(200).send(view); } else { - res.status(404).send(`View ${name} not found`); + util.sendError(res, 404, `View ${name} not found`); } } catch (error) { logger.error('view retrieve error failed', { @@ -1907,36 +1910,29 @@ async function extractFullApiBundleFromUploadedZip(zipFile, orgId, apiId) { const apiMetadata = parseApiMetadataFromYamlFile(path.basename(metadataFilePath), apiMetadataBuffer); const isMcp = apiMetadata.type === constants.API_TYPE.MCP; - // The tools schema (MCP) lives in its own file, distinct from an apiDefinition. - const schemaDefinitionFilePath = await util.findFileByNameRecursive(rootPath, [ - constants.FILE_NAME.SCHEMA_DEFINITION_FILE_NAME, - constants.FILE_NAME.SCHEMA_DEFINITION_YAML_FILE_NAME, - ]); + // The contract lives in a `definition` file. An MCP server's contract IS its tools + // schema (returned as schemaDefinitionFile so the MCP branch stores it under + // SCHEMA_DEFINITION); every other type carries an OpenAPI/WSDL/SDL definition + // (returned as apiDefinitionFile). Both use the same file name — the resolved + // API type decides how it is interpreted. + const definitionCandidates = [ + constants.FILE_NAME.API_DEFINITION_YAML_FILE_NAME, + 'definition.yml', + constants.FILE_NAME.API_DEFINITION_FILE_NAME, + ]; let schemaDefinitionFile; let schemaDefinitionFileName; - if (schemaDefinitionFilePath) { - schemaDefinitionFile = await fs.readFile(schemaDefinitionFilePath); - schemaDefinitionFileName = path.basename(schemaDefinitionFilePath); - } - - // An MCP server's contract IS its schemaDefinition (tools) — it has no apiDefinition, - // so an MCP zip must carry a schemaDefinition and needs no definition file. Every other - // API type requires a definition file and has no schemaDefinition here. let apiDefinitionFile; let apiDefinitionFileName; if (isMcp) { - if (!schemaDefinitionFile) { - throw new Sequelize.ValidationError("Invalid MCP server zip: missing schemaDefinition file (schemaDefinition.yaml/json)"); + const schemaDefinitionFilePath = await util.findFileByNameRecursive(rootPath, definitionCandidates); + if (!schemaDefinitionFilePath) { + throw new Sequelize.ValidationError("Invalid MCP server zip: missing definition file (definition.yaml/yml/json)"); } + schemaDefinitionFile = await fs.readFile(schemaDefinitionFilePath); + schemaDefinitionFileName = path.basename(schemaDefinitionFilePath); } else { - const definitionFilePath = await util.findFileByNameRecursive(rootPath, [ - 'definition.yaml', - 'definition.yml', - 'definition.json', - 'apiDefinition.yaml', - 'apiDefinition.yml', - 'apiDefinition.json', - ]); + const definitionFilePath = await util.findFileByNameRecursive(rootPath, definitionCandidates); if (!definitionFilePath) { throw new Sequelize.ValidationError("Invalid full API zip: missing definition file (definition.yaml/yml/json)"); } @@ -2016,9 +2012,12 @@ function mapDevportalYamlToApiMetadata(parsedYaml) { } function parseApiMetadataFromYamlFile(fileName, fileBuffer) { - const allowedMetadataFileNames = new Set(['api.yaml', 'mcp.yaml', 'devportal.yaml']); + const allowedMetadataFileNames = new Set([ + 'metadata.yaml', 'metadata.yml', 'metadata.json', + 'api.yaml', 'mcp.yaml', 'devportal.yaml', + ]); if (!allowedMetadataFileNames.has(String(fileName).toLowerCase())) { - throw new Sequelize.ValidationError("Invalid metadata file name. Expected 'api.yaml', 'mcp.yaml' or 'devportal.yaml'"); + throw new Sequelize.ValidationError("Invalid metadata file name. Expected 'metadata.yaml', 'metadata.yml', 'metadata.json', 'api.yaml', 'mcp.yaml' or 'devportal.yaml'"); } let parsedYaml; @@ -2032,14 +2031,14 @@ function parseApiMetadataFromYamlFile(fileName, fileBuffer) { } function parseApiMetadataFromYamlRequest(req) { - const apiFile = req.files?.api?.[0]; - if (!apiFile?.buffer) { + const metadataFile = req.files?.metadata?.[0]; + if (!metadataFile?.buffer) { throw new Sequelize.ValidationError( - "Missing required multipart file field: 'api'" + "Missing required multipart file field: 'metadata'" ); } - return parseApiMetadataFromYamlFile(apiFile.originalname, apiFile.buffer); + return parseApiMetadataFromYamlFile(metadataFile.originalname, metadataFile.buffer); } function legacyLimitsFromSpec(spec) { diff --git a/portals/developer-portal/src/services/apiWorkflowService.js b/portals/developer-portal/src/services/apiWorkflowService.js index 95a0993443..f7e226661d 100644 --- a/portals/developer-portal/src/services/apiWorkflowService.js +++ b/portals/developer-portal/src/services/apiWorkflowService.js @@ -186,23 +186,23 @@ const createAPIWorkflow = async (req, res) => { resolvedHandle = `workflow-${suffix}`; } if (id && id.trim() && !HANDLE_PATTERN.test(resolvedHandle)) { - return res.status(400).json({ message: "Invalid 'id'. Must contain only letters, numbers, underscores, and hyphens." }); + return util.sendError(res, 400, "Invalid 'id'. Must contain only letters, numbers, underscores, and hyphens."); } const resolvedContentType = contentType || constants.API_WORKFLOW_CONTENT_TYPE.ARAZZO; if (!Object.values(constants.API_WORKFLOW_CONTENT_TYPE).includes(resolvedContentType)) { - return res.status(400).json({ message: `Invalid contentType. Must be one of: ${Object.values(constants.API_WORKFLOW_CONTENT_TYPE).join(', ')}.` }); + return util.sendError(res, 400, `Invalid contentType. Must be one of: ${Object.values(constants.API_WORKFLOW_CONTENT_TYPE).join(', ')}.`); } if (status && !Object.values(constants.API_WORKFLOW_STATUS).includes(status)) { - return res.status(400).json({ message: `Invalid status. Must be one of: ${Object.values(constants.API_WORKFLOW_STATUS).join(', ')}.` }); + return util.sendError(res, 400, `Invalid status. Must be one of: ${Object.values(constants.API_WORKFLOW_STATUS).join(', ')}.`); } if (agentVisibility && !Object.values(constants.AGENT_VISIBILITY).includes(agentVisibility)) { - return res.status(400).json({ message: `Invalid agentVisibility. Must be one of: ${Object.values(constants.AGENT_VISIBILITY).join(', ')}.` }); + return util.sendError(res, 400, `Invalid agentVisibility. Must be one of: ${Object.values(constants.AGENT_VISIBILITY).join(', ')}.`); } const resolvedContent = resolvedContentType === 'MD' ? (markdownContent || null) : normalizeToJSON(apiWorkflowDefinition); if (resolvedContentType !== 'MD' && resolvedContent === null) { - return res.status(400).json({ message: 'Invalid API workflow definition: content could not be parsed as valid JSON or YAML.' }); + return util.sendError(res, 400, 'Invalid API workflow definition: content could not be parsed as valid JSON or YAML.'); } let t; try { @@ -235,13 +235,13 @@ const createAPIWorkflow = async (req, res) => { } catch (error) { if (t) await t.rollback(); if (error instanceof UniqueConstraintError) { - return res.status(409).json({ message: 'An API workflow with this handle already exists. Please use a different handle.' }); + return util.sendError(res, 409, 'An API workflow with this handle already exists. Please use a different handle.'); } if (error instanceof CustomError) { - return res.status(error.statusCode).json({ message: error.message }); + return util.sendError(res, error.statusCode, error.message); } logger.error('Error creating API Workflow', { error: error.message, stack: error.stack }); - res.status(500).json({ message: constants.ERROR_MESSAGE.API_WORKFLOW_CREATE_ERROR }); + util.sendError(res, 500, constants.ERROR_MESSAGE.API_WORKFLOW_CREATE_ERROR); } }; @@ -251,23 +251,23 @@ const updateAPIWorkflow = async (req, res) => { const userId = util.resolveActor(req); const { displayName, id, description, agentPrompt, status, agentVisibility, apiWorkflowDefinition, markdownContent, contentType } = req.body; if (status !== undefined && !Object.values(constants.API_WORKFLOW_STATUS).includes(status)) { - return res.status(400).json({ message: `Invalid status. Must be one of: ${Object.values(constants.API_WORKFLOW_STATUS).join(', ')}.` }); + return util.sendError(res, 400, `Invalid status. Must be one of: ${Object.values(constants.API_WORKFLOW_STATUS).join(', ')}.`); } if (agentVisibility !== undefined && !Object.values(constants.AGENT_VISIBILITY).includes(agentVisibility)) { - return res.status(400).json({ message: `Invalid agentVisibility. Must be one of: ${Object.values(constants.AGENT_VISIBILITY).join(', ')}.` }); + return util.sendError(res, 400, `Invalid agentVisibility. Must be one of: ${Object.values(constants.AGENT_VISIBILITY).join(', ')}.`); } if (contentType !== undefined && !Object.values(constants.API_WORKFLOW_CONTENT_TYPE).includes(contentType)) { - return res.status(400).json({ message: `Invalid contentType. Must be one of: ${Object.values(constants.API_WORKFLOW_CONTENT_TYPE).join(', ')}.` }); + return util.sendError(res, 400, `Invalid contentType. Must be one of: ${Object.values(constants.API_WORKFLOW_CONTENT_TYPE).join(', ')}.`); } if (id !== undefined && !HANDLE_PATTERN.test(id)) { - return res.status(400).json({ message: "Invalid 'id'. Must contain only letters, numbers, underscores, and hyphens." }); + return util.sendError(res, 400, "Invalid 'id'. Must contain only letters, numbers, underscores, and hyphens."); } const resolvedContentType = contentType; const resolvedContent = resolvedContentType === 'MD' ? (markdownContent !== undefined ? markdownContent : undefined) : (apiWorkflowDefinition !== undefined ? normalizeToJSON(apiWorkflowDefinition) : undefined); if (resolvedContentType !== 'MD' && apiWorkflowDefinition !== undefined && resolvedContent === null) { - return res.status(400).json({ message: 'Invalid API workflow definition: content could not be parsed as valid JSON or YAML.' }); + return util.sendError(res, 400, 'Invalid API workflow definition: content could not be parsed as valid JSON or YAML.'); } const t = await sequelize.transaction(); try { @@ -275,7 +275,7 @@ const updateAPIWorkflow = async (req, res) => { const existing = await apiWorkflowDao.getByHandle(orgId, viewId, apiWorkflowHandle); if (!existing) { await t.rollback(); - return res.status(404).json({ message: constants.ERROR_MESSAGE.API_WORKFLOW_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.API_WORKFLOW_NOT_FOUND); } const [count] = await apiWorkflowDao.update(orgId, viewId, existing.uuid, { displayName, @@ -290,7 +290,7 @@ const updateAPIWorkflow = async (req, res) => { if (count === 0) { await t.rollback(); - return res.status(404).json({ message: constants.ERROR_MESSAGE.API_WORKFLOW_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.API_WORKFLOW_NOT_FOUND); } await t.commit(); @@ -300,13 +300,13 @@ const updateAPIWorkflow = async (req, res) => { } catch (error) { await t.rollback(); if (error instanceof UniqueConstraintError) { - return res.status(409).json({ message: 'An API workflow with this handle already exists. Please use a different handle.' }); + return util.sendError(res, 409, 'An API workflow with this handle already exists. Please use a different handle.'); } if (error instanceof CustomError) { - return res.status(error.statusCode).json({ message: error.message }); + return util.sendError(res, error.statusCode, error.message); } logger.error('Error updating API Workflow', { error: error.message, stack: error.stack }); - res.status(500).json({ message: constants.ERROR_MESSAGE.API_WORKFLOW_UPDATE_ERROR }); + util.sendError(res, 500, constants.ERROR_MESSAGE.API_WORKFLOW_UPDATE_ERROR); } }; @@ -319,12 +319,12 @@ const deleteAPIWorkflow = async (req, res) => { const existing = await apiWorkflowDao.getByHandle(orgId, viewId, apiWorkflowHandle); if (!existing) { await t.rollback(); - return res.status(404).json({ message: constants.ERROR_MESSAGE.API_WORKFLOW_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.API_WORKFLOW_NOT_FOUND); } const count = await apiWorkflowDao.delete(orgId, viewId, existing.uuid, t); if (count === 0) { await t.rollback(); - return res.status(404).json({ message: constants.ERROR_MESSAGE.API_WORKFLOW_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.API_WORKFLOW_NOT_FOUND); } await t.commit(); logger.info('API Workflow deleted', { apiWorkflowId: existing.uuid, orgId, viewId }); @@ -333,10 +333,10 @@ const deleteAPIWorkflow = async (req, res) => { } catch (error) { await t.rollback(); if (error instanceof CustomError) { - return res.status(error.statusCode).json({ message: error.message }); + return util.sendError(res, error.statusCode, error.message); } logger.error('Error deleting API Workflow', { error: error.message, stack: error.stack }); - res.status(500).json({ message: constants.ERROR_MESSAGE.API_WORKFLOW_DELETE_ERROR }); + util.sendError(res, 500, constants.ERROR_MESSAGE.API_WORKFLOW_DELETE_ERROR); } }; @@ -347,16 +347,16 @@ const getAPIWorkflow = async (req, res) => { const viewId = await resolveViewId(orgId, viewHandle); const apiWorkflow = await apiWorkflowDao.getByHandle(orgId, viewId, apiWorkflowHandle); if (!apiWorkflow) { - return res.status(404).json({ message: constants.ERROR_MESSAGE.API_WORKFLOW_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.API_WORKFLOW_NOT_FOUND); } const audit = await userIdpReferenceDao.buildSingleAuditFields(apiWorkflow); res.status(200).json(toAPIWorkflowDTO(apiWorkflow, audit)); } catch (error) { if (error instanceof CustomError) { - return res.status(error.statusCode).json({ message: error.message }); + return util.sendError(res, error.statusCode, error.message); } logger.error('Error fetching API Workflow', { error: error.message, stack: error.stack }); - res.status(500).json({ message: constants.ERROR_MESSAGE.API_WORKFLOW_RETRIEVE_ERROR }); + util.sendError(res, 500, constants.ERROR_MESSAGE.API_WORKFLOW_RETRIEVE_ERROR); } }; @@ -369,10 +369,10 @@ const getAllAPIWorkflows = async (req, res) => { res.status(200).json(util.toPaginatedList(await toAPIWorkflowListDTOs(apiWorkflows), req)); } catch (error) { if (error instanceof CustomError) { - return res.status(error.statusCode).json({ message: error.message }); + return util.sendError(res, error.statusCode, error.message); } logger.error('Error fetching API Workflows', { error: error.message, stack: error.stack }); - res.status(500).json({ message: constants.ERROR_MESSAGE.API_WORKFLOW_RETRIEVE_ERROR }); + util.sendError(res, 500, constants.ERROR_MESSAGE.API_WORKFLOW_RETRIEVE_ERROR); } }; @@ -384,7 +384,7 @@ const generatePrompt = async (req, res) => { res.status(200).json({ agentPrompt: prompt }); } catch (error) { logger.error('Error generating agent prompt', { error: error.message }); - res.status(500).json({ message: 'Error generating agent prompt' }); + util.sendError(res, 500, 'Error generating agent prompt'); } }; diff --git a/portals/developer-portal/src/services/devportalService.js b/portals/developer-portal/src/services/devportalService.js index 83f9f180d6..2a39b79ab1 100644 --- a/portals/developer-portal/src/services/devportalService.js +++ b/portals/developer-portal/src/services/devportalService.js @@ -58,7 +58,7 @@ const getOrgContent = async (req, res) => { res.set(constants.MIME_TYPES.CONYEMT_TYPE, contentType); return res.status(200).send(Buffer.isBuffer(asset.file_content) ? asset.file_content : constants.CHARSET_UTF8); } else { - return res.status(404).send('Not Found'); + return util.sendError(res, 404, 'Not Found'); } } else if (req.params.fileType) { const assets = await adminService.getOrgContent(req.orgId, req.params.viewId, req.params.fileType); @@ -73,7 +73,7 @@ const getOrgContent = async (req, res) => { } return res.status(200).send(results); } else { - res.status(400).send('Invalid request'); + util.sendError(res, 400, 'Invalid request'); } } catch (error) { logger.error('Error while fetching organization content', { @@ -82,7 +82,7 @@ const getOrgContent = async (req, res) => { orgId: req.orgId, viewId: req.params.viewId }); - res.status(404).send(error.message); + return util.sendError(res, 500, 'Internal Server Error'); } }; diff --git a/portals/developer-portal/src/services/keyManagerService.js b/portals/developer-portal/src/services/keyManagerService.js index f43d963524..01545314d9 100644 --- a/portals/developer-portal/src/services/keyManagerService.js +++ b/portals/developer-portal/src/services/keyManagerService.js @@ -139,12 +139,12 @@ const createKeyManager = async (req, res) => { const validationError = _validateRequiredFields(payload); if (validationError) { - return res.status(400).json({ error: validationError }); + return util.sendError(res, 400, validationError); } const hadExplicitHandle = !!(payload.handle && payload.handle.trim()); const resolvedHandle = hadExplicitHandle ? payload.handle.trim() : generateHandle(payload.displayName); if (!resolvedHandle || !HANDLE_PATTERN.test(resolvedHandle)) { - return res.status(400).json({ error: "Invalid 'id'. Must contain only letters, numbers, underscores, and hyphens." }); + return util.sendError(res, 400, "Invalid 'id'. Must contain only letters, numbers, underscores, and hyphens."); } const userId = util.resolveActor(req); @@ -164,15 +164,13 @@ const createKeyManager = async (req, res) => { return res.status(201).json(dto); } catch (error) { if (error instanceof Sequelize.UniqueConstraintError) { - return res.status(409).json({ - error: `A key manager with id "${req.body?.id}" already exists in this organization.` - }); + return util.sendError(res, 409, `A key manager with that id already exists in this organization.`); } if (error.name === 'YAMLException' || error.name === 'ValidationError') { - return res.status(400).json({ error: error.message }); + return util.sendError(res, 400, 'Invalid payload format or validation failed.'); } logger.error(constants.ERROR_MESSAGE.KEY_MANAGER_CREATE_ERROR, { error }); - return res.status(500).json({ error: constants.ERROR_MESSAGE.KEY_MANAGER_CREATE_ERROR }); + return util.sendError(res, 500, constants.ERROR_MESSAGE.KEY_MANAGER_CREATE_ERROR); } }; @@ -186,7 +184,7 @@ const updateKeyManager = async (req, res) => { const kmId = await kmDao.getIdByHandle(orgId, kmHandle); if (!kmId) { - return res.status(404).json({ error: constants.ERROR_MESSAGE.KEY_MANAGER_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.KEY_MANAGER_NOT_FOUND); } const userId = util.resolveActor(req); @@ -206,18 +204,16 @@ const updateKeyManager = async (req, res) => { return res.status(200).json(dto); } catch (error) { if (error instanceof Sequelize.EmptyResultError) { - return res.status(404).json({ error: constants.ERROR_MESSAGE.KEY_MANAGER_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.KEY_MANAGER_NOT_FOUND); } if (error instanceof Sequelize.UniqueConstraintError) { - return res.status(409).json({ - error: `A key manager with that id already exists in this organization.` - }); + return util.sendError(res, 409, `A key manager with that id already exists in this organization.`); } if (error.name === 'YAMLException' || error.name === 'ValidationError') { - return res.status(400).json({ error: error.message }); + return util.sendError(res, 400, 'Invalid payload format or validation failed.'); } logger.error(constants.ERROR_MESSAGE.KEY_MANAGER_UPDATE_ERROR, { error }); - return res.status(500).json({ error: constants.ERROR_MESSAGE.KEY_MANAGER_UPDATE_ERROR }); + return util.sendError(res, 500, constants.ERROR_MESSAGE.KEY_MANAGER_UPDATE_ERROR); } }; @@ -235,7 +231,7 @@ const getKeyManagers = async (req, res) => { return res.status(200).json(util.toPaginatedList(dtos, req)); } catch (error) { logger.error(constants.ERROR_MESSAGE.KEY_MANAGER_RETRIEVE_ERROR, { error }); - return res.status(500).json({ error: constants.ERROR_MESSAGE.KEY_MANAGER_RETRIEVE_ERROR }); + return util.sendError(res, 500, constants.ERROR_MESSAGE.KEY_MANAGER_RETRIEVE_ERROR); } }; @@ -245,7 +241,7 @@ const getKeyManager = async (req, res) => { const { kmId: kmHandle } = req.params; const kmId = await kmDao.getIdByHandle(orgId, kmHandle); if (!kmId) { - return res.status(404).json({ error: constants.ERROR_MESSAGE.KEY_MANAGER_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.KEY_MANAGER_NOT_FOUND); } const record = await kmDao.get(kmId); const audit = await userIdpReferenceDao.buildSingleAuditFields(record); @@ -253,10 +249,10 @@ const getKeyManager = async (req, res) => { return res.status(200).json(dto); } catch (error) { if (error instanceof Sequelize.EmptyResultError) { - return res.status(404).json({ error: constants.ERROR_MESSAGE.KEY_MANAGER_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.KEY_MANAGER_NOT_FOUND); } logger.error(constants.ERROR_MESSAGE.KEY_MANAGER_RETRIEVE_ERROR, { error }); - return res.status(500).json({ error: constants.ERROR_MESSAGE.KEY_MANAGER_RETRIEVE_ERROR }); + return util.sendError(res, 500, constants.ERROR_MESSAGE.KEY_MANAGER_RETRIEVE_ERROR); } }; @@ -266,17 +262,17 @@ const deleteKeyManager = async (req, res) => { const { kmId: kmHandle } = req.params; const kmId = await kmDao.getIdByHandle(orgId, kmHandle); if (!kmId) { - return res.status(404).json({ error: constants.ERROR_MESSAGE.KEY_MANAGER_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.KEY_MANAGER_NOT_FOUND); } await kmDao.delete(kmId); logUserAction('KEY_MANAGER_DELETED', req, { orgId, kmId, resourceUuid: kmId, resourceType: 'key_manager' }); return res.status(204).send(); } catch (error) { if (error instanceof Sequelize.EmptyResultError) { - return res.status(404).json({ error: constants.ERROR_MESSAGE.KEY_MANAGER_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.KEY_MANAGER_NOT_FOUND); } logger.error(constants.ERROR_MESSAGE.KEY_MANAGER_DELETE_ERROR, { error }); - return res.status(500).json({ error: constants.ERROR_MESSAGE.KEY_MANAGER_DELETE_ERROR }); + return util.sendError(res, 500, constants.ERROR_MESSAGE.KEY_MANAGER_DELETE_ERROR); } }; diff --git a/portals/developer-portal/src/services/mcpRegistryService.js b/portals/developer-portal/src/services/mcpRegistryService.js index d6f9ac52f0..c8fd771ae8 100644 --- a/portals/developer-portal/src/services/mcpRegistryService.js +++ b/portals/developer-portal/src/services/mcpRegistryService.js @@ -35,7 +35,7 @@ const VERSION_RANGE_PATTERN = /^[\^~]|^>=?|^<=?|\*|(^|\.)x(\.|$)/i; const DEFAULT_LIMIT = 30; const MAX_LIMIT = 100; // Canonical stored schema filename/shape across the whole platform: a flat, type-tagged -// YAML array (schemaDefinition.yaml) — the same format the admin /mcp-servers API and the +// YAML array (definition.yaml) — the same format the admin /mcp-servers API and the // sample seeder write. Registry writes arrive grouped and are flattened to this on write // (see toFlatSchema); parseSchema regroups on read. const SCHEMA_FILE_NAME = constants.FILE_NAME.SCHEMA_DEFINITION_YAML_FILE_NAME; @@ -199,7 +199,7 @@ function buildApiMetadataPayload(name, version, description, remotes, title, pub referenceId: null, name: name, handle: apiHandle, - apiTitle: title || null, + title: title || null, description: description || `${name} MCP proxy`, version: version, type: constants.API_TYPE.MCP, diff --git a/portals/developer-portal/src/services/mcpServerService.js b/portals/developer-portal/src/services/mcpServerService.js index 541c952ba4..ee2039b262 100644 --- a/portals/developer-portal/src/services/mcpServerService.js +++ b/portals/developer-portal/src/services/mcpServerService.js @@ -71,7 +71,7 @@ const getAllMcpServersForOrganization = async (req, res) => { try { const orgId = req.orgId; const searchTerm = req.query.query; - const apiName = req.query.apiName; + const apiName = req.query.name; const apiVersion = req.query.version; const tags = req.query.tags; const view = req.query.view; diff --git a/portals/developer-portal/src/services/sampleSeederService.js b/portals/developer-portal/src/services/sampleSeederService.js index 37fc639b90..8e6863b412 100644 --- a/portals/developer-portal/src/services/sampleSeederService.js +++ b/portals/developer-portal/src/services/sampleSeederService.js @@ -170,7 +170,7 @@ async function seedSampleAPIs(orgId) { /** * Deploy all sample MCP servers from samples/mcps/ into the given org. - * Each subdirectory must contain api.yaml and optionally schemaDefinition.yaml and docs/. + * Each subdirectory must contain api.yaml and optionally definition.yaml and docs/. * Returns an array of { name, status ('ok'|'exists'|'failed'), apiId?, error? }. */ async function seedSampleMCPs(orgId) { diff --git a/portals/developer-portal/src/services/subscriptionService.js b/portals/developer-portal/src/services/subscriptionService.js index 4d2f055cda..14150e1820 100644 --- a/portals/developer-portal/src/services/subscriptionService.js +++ b/portals/developer-portal/src/services/subscriptionService.js @@ -72,7 +72,7 @@ function formatSubscriptionResponse(sub, audit) { subscriptionId: sub.uuid, subscriptionToken: sub.token, status: sub.status, - apiId: api.handle || sub.api_uuid, + artifactId: api.handle || sub.api_uuid, subscriptionPlanName: plan.display_name || null, ...audit, }; @@ -80,46 +80,34 @@ function formatSubscriptionResponse(sub, audit) { const createSubscription = async (req, res) => { const orgId = req.orgId; - const { apiId: apiHandle, subscriptionPlanId: reqPlanHandle } = req.body; + const { artifactId: apiHandle, subscriptionPlanId: reqPlanHandle } = req.body; const createdBy = util.resolveActor(req); let apiId; if (!apiHandle || typeof apiHandle !== 'string' || !apiHandle.trim()) { - return res.status(400).json({ - code: '400', message: 'Bad Request', description: 'apiId is required', - }); + return util.sendError(res, 400, 'Bad Request', { errors: [{ message: 'artifactId is required' }] }); } try { apiId = await apiDao.getId(orgId, apiHandle); if (!apiId) { - return res.status(404).json({ - code: '404', message: 'Not Found', description: 'API not found', - }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'API not found' }] }); } const apiMetadataResponse = await apiDao.get(orgId, apiId); if (!apiMetadataResponse || apiMetadataResponse.length === 0) { - return res.status(404).json({ - code: '404', message: 'Not Found', description: 'API not found', - }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'API not found' }] }); } const apiMetadata = apiMetadataResponse[0]; const plans = apiMetadata.dp_subscription_plans || []; if (plans.length === 0) { - return res.status(400).json({ - code: '400', message: 'Bad Request', - description: 'This API does not support subscriptions', - }); + return util.sendError(res, 400, 'Bad Request', { errors: [{ message: 'This API does not support subscriptions' }] }); } const matchedPlan = plans.find(p => p.handle === reqPlanHandle); if (!matchedPlan) { - return res.status(400).json({ - code: '400', message: 'Bad Request', - description: 'Subscription plan not found for this API', - }); + return util.sendError(res, 400, 'Bad Request', { errors: [{ message: 'Subscription plan not found for this API' }] }); } const planId = matchedPlan.uuid; @@ -143,10 +131,7 @@ const createSubscription = async (req, res) => { return res.status(201).json(formatSubscriptionResponse(created, audit)); } catch (error) { if (error.name === 'SequelizeUniqueConstraintError') { - return res.status(409).json({ - code: '409', message: 'Conflict', - description: 'A subscription for this API already exists', - }); + return util.sendError(res, 409, 'Conflict', { errors: [{ message: 'A subscription for this API already exists' }] }); } logger.error('Error creating subscription', { error: error.message, orgId, apiId, @@ -157,16 +142,14 @@ const createSubscription = async (req, res) => { const listSubscriptions = async (req, res) => { const orgId = req.orgId; - const apiHandle = req.query.apiId; + const apiHandle = req.query.artifactId; let apiId; try { if (apiHandle) { apiId = await apiDao.getId(orgId, apiHandle); if (!apiId) { - return res.status(404).json({ - code: '404', message: 'Not Found', description: 'API not found', - }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'API not found' }] }); } } @@ -188,9 +171,7 @@ const getSubscription = async (req, res) => { try { const sub = await subDao.get(orgId, subscriptionId, util.resolveActor(req)); if (!sub) { - return res.status(404).json({ - code: '404', message: 'Not Found', description: 'Subscription not found', - }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Subscription not found' }] }); } const audit = await userIdpReferenceDao.buildSingleAuditFields(sub); return res.status(200).json(formatSubscriptionResponse(sub, audit)); @@ -207,16 +188,14 @@ const updateSubscription = async (req, res) => { const subscriptionId = req.params.subId; const { status } = req.body; if (!Object.values(constants.SUBSCRIPTION_STATUS).includes(status)) { - return res.status(400).json({ code: '400', message: 'Bad Request', description: `Invalid status. Must be one of: ${Object.values(constants.SUBSCRIPTION_STATUS).join(', ')}.` }); + return util.sendError(res, 400, 'Bad Request', { errors: [{ message: `Invalid status. Must be one of: ${Object.values(constants.SUBSCRIPTION_STATUS).join(', ')}.` }] }); } const actorId = util.resolveActor(req); try { const existing = await subDao.get(orgId, subscriptionId, actorId); if (!existing) { - return res.status(404).json({ - code: '404', message: 'Not Found', description: 'Subscription not found', - }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Subscription not found' }] }); } let sub; @@ -236,7 +215,7 @@ const updateSubscription = async (req, res) => { return res.status(200).json(formatSubscriptionResponse(sub, audit)); } catch (error) { if (error.status === 404) { - return res.status(404).json({ code: '404', message: 'Not Found', description: 'Subscription not found' }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Subscription not found' }] }); } logger.error('Error updating subscription', { error: error.message, subscriptionId, status, @@ -248,43 +227,33 @@ const updateSubscription = async (req, res) => { const changePlan = async (req, res) => { const orgId = req.orgId; const subscriptionId = req.params.subId; - const { apiId: reqApiHandle, planId: reqPlanHandle } = req.body; + const { artifactId: reqApiHandle, planId: reqPlanHandle } = req.body; const actorId = util.resolveActor(req); try { const existing = await subDao.get(orgId, subscriptionId, actorId); if (!existing) { - return res.status(404).json({ - code: '404', message: 'Not Found', description: 'Subscription not found', - }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Subscription not found' }] }); } const apiId = existing.api_uuid || (existing.dp_api_metadata ? existing.dp_api_metadata.uuid : null) || null; if (!apiId) { - return res.status(400).json({ - code: '400', message: 'Bad Request', description: 'API not found for this subscription', - }); + return util.sendError(res, 400, 'Bad Request', { errors: [{ message: 'API not found for this subscription' }] }); } const apiHandle = existing.dp_api_metadata ? existing.dp_api_metadata.handle : null; if (reqApiHandle && reqApiHandle !== apiHandle) { - return res.status(400).json({ - code: '400', message: 'Bad Request', description: 'apiId does not match this subscription', - }); + return util.sendError(res, 400, 'Bad Request', { errors: [{ message: 'artifactId does not match this subscription' }] }); } const apiMetadataResponse = await apiDao.get(orgId, apiId); if (!apiMetadataResponse || apiMetadataResponse.length === 0) { - return res.status(404).json({ - code: '404', message: 'Not Found', description: 'API not found', - }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'API not found' }] }); } const apiMetadata = apiMetadataResponse[0]; const plans = apiMetadata.dp_subscription_plans || []; const newPlan = plans.find(p => p.handle === reqPlanHandle); if (!newPlan) { - return res.status(400).json({ - code: '400', message: 'Bad Request', description: 'Subscription plan not found for this API', - }); + return util.sendError(res, 400, 'Bad Request', { errors: [{ message: 'Subscription plan not found for this API' }] }); } const planId = newPlan.uuid; @@ -315,7 +284,7 @@ const changePlan = async (req, res) => { return res.status(200).json(formatSubscriptionResponse(updated, audit)); } catch (error) { if (error.status === 404) { - return res.status(404).json({ code: '404', message: 'Not Found', description: 'Subscription not found' }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Subscription not found' }] }); } logger.error('Error changing subscription plan', { error: error.message, subscriptionId }); util.handleError(res, error); @@ -330,9 +299,7 @@ const regenerateSubscriptionToken = async (req, res) => { try { const existing = await subDao.get(orgId, subscriptionId, actorId); if (!existing) { - return res.status(404).json({ - code: '404', message: 'Not Found', description: 'Subscription not found', - }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Subscription not found' }] }); } const apiMetadata = existing.dp_api_metadata; @@ -358,7 +325,7 @@ const regenerateSubscriptionToken = async (req, res) => { return res.status(200).json(formatSubscriptionResponse(updated, audit)); } catch (error) { if (error.status === 404) { - return res.status(404).json({ code: '404', message: 'Not Found', description: 'Subscription not found' }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Subscription not found' }] }); } logger.error('Error regenerating subscription token', { error: error.message, subscriptionId }); util.handleError(res, error); @@ -373,9 +340,7 @@ const deleteSubscription = async (req, res) => { try { const existing = await subDao.get(orgId, subscriptionId, actorId); if (!existing) { - return res.status(404).json({ - code: '404', message: 'Not Found', description: 'Subscription not found', - }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Subscription not found' }] }); } const apiMetadata = existing.dp_api_metadata; @@ -396,9 +361,7 @@ const deleteSubscription = async (req, res) => { return res.status(200).json({ message: 'Subscription deleted successfully' }); } catch (error) { if (error.statusCode === 404) { - return res.status(404).json({ - code: '404', message: 'Not Found', description: 'Subscription not found', - }); + return util.sendError(res, 404, 'Not Found', { errors: [{ message: 'Subscription not found' }] }); } logger.error('Error deleting subscription', { error: error.message, subscriptionId, diff --git a/portals/developer-portal/src/services/webhookSubscriberService.js b/portals/developer-portal/src/services/webhookSubscriberService.js index a745220753..6169d98a87 100644 --- a/portals/developer-portal/src/services/webhookSubscriberService.js +++ b/portals/developer-portal/src/services/webhookSubscriberService.js @@ -57,7 +57,7 @@ const createWebhookSubscriber = async (req, res) => { const validationError = _validateRequiredFields(payload); if (validationError) { - return res.status(400).json({ error: validationError }); + return util.sendError(res, 400, validationError); } const userId = util.resolveActor(req); @@ -68,12 +68,10 @@ const createWebhookSubscriber = async (req, res) => { return res.status(201).json(dto); } catch (error) { if (error instanceof Sequelize.UniqueConstraintError) { - return res.status(409).json({ - error: _uniqueConstraintMessage(error, req.body) - }); + return util.sendError(res, 409, _uniqueConstraintMessage(error, req.body)); } logger.error(constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_CREATE_ERROR, { error }); - return res.status(500).json({ error: constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_CREATE_ERROR }); + return util.sendError(res, 500, constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_CREATE_ERROR); } }; @@ -94,15 +92,13 @@ const updateWebhookSubscriber = async (req, res) => { return res.status(200).json(dto); } catch (error) { if (error instanceof Sequelize.EmptyResultError) { - return res.status(404).json({ error: constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_NOT_FOUND); } if (error instanceof Sequelize.UniqueConstraintError) { - return res.status(409).json({ - error: _uniqueConstraintMessage(error, req.body) - }); + return util.sendError(res, 409, _uniqueConstraintMessage(error, req.body)); } logger.error(constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_UPDATE_ERROR, { error }); - return res.status(500).json({ error: constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_UPDATE_ERROR }); + return util.sendError(res, 500, constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_UPDATE_ERROR); } }; @@ -115,7 +111,7 @@ const getWebhookSubscribers = async (req, res) => { return res.status(200).json(util.toPaginatedList(dtos, req)); } catch (error) { logger.error(constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_RETRIEVE_ERROR, { error }); - return res.status(500).json({ error: constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_RETRIEVE_ERROR }); + return util.sendError(res, 500, constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_RETRIEVE_ERROR); } }; @@ -129,10 +125,10 @@ const getWebhookSubscriber = async (req, res) => { return res.status(200).json(dto); } catch (error) { if (error instanceof Sequelize.EmptyResultError) { - return res.status(404).json({ error: constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_NOT_FOUND); } logger.error(constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_RETRIEVE_ERROR, { error }); - return res.status(500).json({ error: constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_RETRIEVE_ERROR }); + return util.sendError(res, 500, constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_RETRIEVE_ERROR); } }; @@ -156,13 +152,13 @@ const getWebhookSubscriberDeliveries = async (req, res) => { const { subscriberId } = req.params; const sub = await whDao.get(orgId, subscriberId); const deliveries = await eventDao.listDeliveriesForSubscriber(orgId, sub.uuid, 20); - return res.status(200).json({ list: deliveries.map(_formatDeliverySummary) }); + return res.status(200).json(util.toPaginatedList(deliveries.map(_formatDeliverySummary), req)); } catch (error) { if (error instanceof Sequelize.EmptyResultError) { - return res.status(404).json({ error: constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_NOT_FOUND); } logger.error('Error fetching webhook subscriber deliveries', { error }); - return res.status(500).json({ error: 'Failed to fetch webhook subscriber deliveries' }); + return util.sendError(res, 500, 'Failed to fetch webhook subscriber deliveries'); } }; @@ -176,10 +172,10 @@ const deleteWebhookSubscriber = async (req, res) => { return res.status(204).send(); } catch (error) { if (error instanceof Sequelize.EmptyResultError) { - return res.status(404).json({ error: constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_NOT_FOUND }); + return util.sendError(res, 404, constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_NOT_FOUND); } logger.error(constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_DELETE_ERROR, { error }); - return res.status(500).json({ error: constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_DELETE_ERROR }); + return util.sendError(res, 500, constants.ERROR_MESSAGE.WEBHOOK_SUBSCRIBER_DELETE_ERROR); } }; diff --git a/portals/developer-portal/src/utils/constants.js b/portals/developer-portal/src/utils/constants.js index d526288d29..4e4ece3197 100644 --- a/portals/developer-portal/src/utils/constants.js +++ b/portals/developer-portal/src/utils/constants.js @@ -226,13 +226,13 @@ module.exports = { API_DOC_HBS: 'api-doc.hbs', API_CONTENT_PARTIAL_NAME: "api-content", API_DOC_PARTIAL_NAME: "api-doc", - API_DEFINITION_FILE_NAME: 'apiDefinition.json', - API_DEFINITION_YAML_FILE_NAME: 'apiDefinition.yaml', - SCHEMA_DEFINITION_FILE_NAME: 'schemaDefinition.json', - SCHEMA_DEFINITION_YAML_FILE_NAME: 'schemaDefinition.yaml', + API_DEFINITION_FILE_NAME: 'definition.json', + API_DEFINITION_YAML_FILE_NAME: 'definition.yaml', + SCHEMA_DEFINITION_FILE_NAME: 'definition.json', + SCHEMA_DEFINITION_YAML_FILE_NAME: 'definition.yaml', API_SPECIFICATION_PATH: 'specification', - API_DEFINITION_GRAPHQL: 'apiDefinition.graphql', - API_DEFINITION_XML: 'apiDefinition.xml', + API_DEFINITION_GRAPHQL: 'definition.graphql', + API_DEFINITION_XML: 'definition.xml', LLMS_CONFIG: 'llms-config.json', }, ARTIFACT_DIR: { diff --git a/portals/developer-portal/src/utils/sampleApiLoader.js b/portals/developer-portal/src/utils/sampleApiLoader.js index 3a6e206b3b..877a21d625 100644 --- a/portals/developer-portal/src/utils/sampleApiLoader.js +++ b/portals/developer-portal/src/utils/sampleApiLoader.js @@ -208,7 +208,7 @@ function getDefinition(apiHandle, samplesDir = './samples/apis/') { } /** - * Load and parse the MCP schema definition file (schemaDefinition.yaml or .json). + * Load and parse the MCP schema definition file (definition.yaml or .json). * Returns { tools, resources, prompts } or null if not found. */ function getMcpSchema(apiHandle, samplesDir = './samples/apis/') { diff --git a/portals/developer-portal/src/utils/util.js b/portals/developer-portal/src/utils/util.js index 31ac6ebc40..b2b518d7d6 100644 --- a/portals/developer-portal/src/utils/util.js +++ b/portals/developer-portal/src/utils/util.js @@ -280,12 +280,41 @@ function handleError(res, error) { } } +/** + * Emit a standard error response body: + * { status: 'error', code, message, errors, [details], [trackingId] } + * `code` defaults to the HTTP-status catalog entry; pass opts.code to override + * with a resource-specific code. `errors` is always present (empty array when + * there are no field-level errors), matching handleError's shape. + */ +function sendError(res, statusCode, message, opts = {}) { + const body = { + status: 'error', + code: opts.code || HTTP_CODE_TO_CATALOG[statusCode] || 'INTERNAL_SERVER_ERROR', + message, + errors: opts.errors || [], + }; + if (opts.details) body.details = opts.details; + if (opts.trackingId) body.trackingId = opts.trackingId; + return res.status(statusCode).json(body); +} + +/** + * Wrap a fully-materialized list in the standard collection envelope: + * { count, list, pagination: { limit, offset, total } } + * `total` is the grand total across all pages; `count` is the number of items + * returned in this page. limit/offset are applied here (in-memory) so every + * list endpoint paginates consistently. + */ function toPaginatedList(list, req) { - const limit = Math.min(parseInt((req.query && req.query.limit) || '20', 10) || 20, 100); - const offset = parseInt((req.query && req.query.offset) || '0', 10) || 0; + const total = list.length; + const limit = Math.min(Math.max(parseInt((req.query && req.query.limit) || '20', 10) || 20, 0), 100); + const offset = Math.max(parseInt((req.query && req.query.offset) || '0', 10) || 0, 0); + const page = list.slice(offset, offset + limit); return { - list, - pagination: { total: list.length, limit, offset }, + count: page.length, + list: page, + pagination: { limit, offset, total }, }; } @@ -1094,6 +1123,7 @@ module.exports = { renderLlmsTxt, renderGivenTemplate, handleError, + sendError, retrieveContentType, getAPIFileContent, getAPIImages, diff --git a/tests/integration-e2e/steps_devportal_test.go b/tests/integration-e2e/steps_devportal_test.go index 2e742a91d4..477c952201 100644 --- a/tests/integration-e2e/steps_devportal_test.go +++ b/tests/integration-e2e/steps_devportal_test.go @@ -264,10 +264,10 @@ paths: var buf bytes.Buffer mw := multipart.NewWriter(&buf) - if err := addYAMLPart(mw, "api", "api.yaml", apiYAML); err != nil { + if err := addYAMLPart(mw, "metadata", "metadata.yaml", apiYAML); err != nil { return "", nil, err } - if err := addYAMLPart(mw, "apiDefinition", "definition.yaml", defYAML); err != nil { + if err := addYAMLPart(mw, "definition", "definition.yaml", defYAML); err != nil { return "", nil, err } if err := mw.Close(); err != nil { @@ -295,7 +295,7 @@ func (w *world) subscribeInDevportal() error { } st, body, err = dpCall(http.MethodPost, "/subscriptions", map[string]any{ - "apiId": w.dpApiID, // the devportal API handle, not the referenceId + "artifactId": w.dpApiID, // the devportal API handle, not the referenceId "subscriptionPlanId": w.planID, }) if err != nil {