From 5d59a75a357bc6444cedbf4ddfbd53a195d21b2f Mon Sep 17 00:00:00 2001 From: "amilisic@whispir.com" Date: Tue, 14 Oct 2025 19:33:09 +1100 Subject: [PATCH 01/11] CORE-2022: Updated WhatsApp documentation to include changes and examples added for CGP WhatsApp Committed by Whispir-Softwares on behalf of amilisic@whispir.com --- .spectral.yml | 451 -------------------------------------------------- openapi.yaml | 128 ++++++++++++-- 2 files changed, 116 insertions(+), 463 deletions(-) delete mode 100644 .spectral.yml diff --git a/.spectral.yml b/.spectral.yml deleted file mode 100644 index d5bed2a..0000000 --- a/.spectral.yml +++ /dev/null @@ -1,451 +0,0 @@ -extends: spectral:oas - -functions: - - oasOpIdFormat - - oasOpSdk - -aliases: - Operation_List_Schema: - description: The schema of all operations that list resources, e.g. GET /resources - targets: - - formats: - - oas3 - given: - - "#Operation_List.responses[?(@property >= 200 && @property < 300)].content[*].schema" - Operation_List: - description: All operations that list resources, e.g. `GET /resources` - targets: - - formats: - - oas3 - given: - - "$.paths[?(!(@property).endsWith('}'))].get" - -rules: - openapi-tags-alphabetical: off - operation-success-response: error - oas2-operation-formData-consume-check: error - operation-parameters: error - operation-tag-defined: error - contact-properties: error - duplicated-entry-in-enum: error - info-contact: error - info-description: error - info-license: error - license-url: error - no-eval-in-markdown: error - no-script-tags-in-markdown: error - openapi-tags: error - operation-description: error - operation-operationId: error - operation-operationId-valid-in-url: error - operation-singular-tag: error - operation-tags: error - path-declarations-must-exist: error - path-keys-no-trailing-slash: error - path-not-include-query: error - tag-description: error - typed-enum: error - oas2-api-host: error - oas2-api-schemes: error - oas2-host-not-example: error - oas2-host-trailing-slash: error - oas2-parameter-description: error - oas2-operation-security-defined: error - oas2-anyOf: error - oas2-oneOf: error - oas2-unused-definition: error - oas3-api-servers: error - oas3-examples-value-or-externalValue: error - oas3-operation-security-defined: error - oas3-parameter-description: error - oas3-server-not-example.com: error - oas3-server-trailing-slash: error - oas3-unused-component: error - - ### CORE RULES ### - schema-examples: - description: Schemas must have non-empty "x-examples" object. - severity: error - given: $.components.schemas[*] - then: - field: x-examples - function: schema - functionOptions: - dialect: draft7 - schema: - type: object - minProperties: 1 - schema-descriptions: - description: Schemas must have a "description". - severity: error - given: - - $..schemas[*] - - $..responses[*].content[*]..schema - - $..requestBody.content[*]..schema - then: - field: description - function: truthy - schema-property-descriptions: # Note: this rule is not de-duplicated, and will show on parents $ref'ing children with issues until all child issues are resolved. - description: Schema properties must have a "description". - severity: error - given: - - $..schema[*]..properties[*] - - $..responses[*].content[*]..schema..properties[*] - - $..requestBody.content[*]..schema..properties[*] - - $..headers[*] - then: - field: description - function: truthy - schema-property-examples: # Note: this rule is not de-duplicated, and will show on parents $ref'ing children with issues until all child issues are resolved. - description: Schema properties must have an "example". - severity: error - given: - - $..schema[*]..properties[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] - - $..responses[*].content[*]..schema..properties[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] - - $..requestBody.content[*]..schema..properties[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] - - $..headers[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] - then: - field: example - function: defined - payload-examples: - description: Request & response payload content must have non-empty "examples" object. - severity: error - given: - - $..responses[*].content[*] - - $..requestBody.content[*] - then: - field: examples - function: schema - functionOptions: - dialect: draft7 - schema: - type: object - minProperties: 1 - - ### API SPECIFIC RULES ### - required-all-request-headers: - given: - - "$.paths[*][get,put,post,delete,options,head,patch,trace]" - severity: error - then: - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^X-Api-Key$" - field: parameters - description: 'All requests must have the following headers "parameters": "X-Api-Key".' - required-read-request-headers: - given: - - "$.paths[*][get]" - severity: error - then: - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^Accept$" - field: parameters - description: 'Read requests must have the following headers "parameters": "Accept".' - required-write-request-headers: - given: - - "$.paths[*][put,post]" - severity: error - then: - - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^Content-Type$" - field: parameters - - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^Accept$" - field: parameters - description: - 'Write requests must have the following headers "parameters": "Content-Type", - "Accept".' - required-list-pagination-request-query-parameters: - given: - - "$.paths[?(!/{.*Id}$/i.test(@property))]['get']" - severity: error - then: - - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^limit$" - field: parameters - - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^offset$" - field: parameters - - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^sortOrder$" - field: parameters - - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^sortFields$" - field: parameters - description: - 'Read list requests must have the following query "parameters": "limit", - "offset", "sortOrder", "sortFields".' - required-all-response-headers: - given: - - "$..responses[*]" - severity: error - then: - function: schema - functionOptions: - dialect: draft7 - schema: - type: object - required: - - Access-Control-Allow-Origin - - Expires - - Cache-Control - field: headers - description: - 'All responses must have the following "headers": "Access-Control-Allow-Origin", - "Expires", "Cache-Control".' - required-content-response-headers: - given: - - "$.paths[*]..responses[?(@property != 204)]" - severity: error - then: - function: schema - functionOptions: - dialect: draft7 - schema: - type: object - required: - - Content-Type - - Content-Length - field: headers - description: - 'Responses with content must have the following "headers": "Content-Type", - "Content-Length".' - required-post-response-headers: - given: - - "$.paths[*][post]..responses[?(@property != 204 && @property >= 200 && @property - < 300)]" - severity: error - then: - function: schema - functionOptions: - dialect: draft7 - schema: - type: object - required: - - Location - field: headers - description: - 'Successful post endpoint responses must have the following "headers": - "Location".' - operation-sdkOperation-format: - given: - - "$" - severity: error - then: - function: oasOpSdk - functionOptions: - exceptions: [] - description: |- - All operations must include an "x-sdkOperation" vendor extension for use in human-readable SDK method names. - - Must start with one of the following values, corresponding to the operation on the resource: - * `create` - `POST /resource` - * `retrieve` - `GET /resource/{resourceId}` - * `update` - `PUT /resource/{resourceId}` - * `delete` - `DELETE /resource/{resourceId}` - * `list` - `GET /resource` - - An optional noun suffix describing any sub-resources may be included as appropriate. - - **Valid Example** - - ```json - { - "x-sdkOperation": "create" - } - // OR - { - "x-sdkOperation": "listWebhookCalls" - } - ``` - - **Invalid Example** - - ```json - { - "x-sdkOperation": "post" - } - // OR - { - "x-sdkOperation": "getWebhookCalls" - } - ``` - message: "{{error}}" - operation-operationId-format: - given: - - "$" - severity: error - then: - function: oasOpIdFormat - functionOptions: - exceptions: [] - description: |- - All operations must include an appropriately named "operationId". - - Must end with one of the following values, corresponding to the operation on the - resource: - * `Create` - `POST /resource` - * `Retrieve` - `GET /resource/{resourceId}` - * `Update` - `PUT /resource/{resourceId}` - * `Delete` - `DELETE /resource/{resourceId}` - * `List` - `GET /resource` - - An optional noun prefix describing any sub-resources - may be included as appropriate. - - **Valid Example** - - ```json - { - "operationId": "messageCreate" - } - // OR - { - "operationId": "webhookCallList" - } - ``` - - **Invalid Example** - - ```json - { - "operationId": "getMessage" - } - // OR - { - "operationId": "getWebhookCalls" - } - ``` - message: "{{error}}" - operation-list-schema-keys: - given: - - "#Operation_List_Schema" - severity: error - then: - function: pattern - functionOptions: - match: Collection$ - field: "$ref" - description: List operation response schema must have key ending with "Collection". - message: List operation response schema must have key ending with "Collection". - operation-list-schema-title: - given: - - "#Operation_List_Schema" - severity: error - then: - function: pattern - functionOptions: - match: Collection$ - field: title - description: |- - List operation response schema must have title ending with "Collection". - - **Valid Example** - - ```json - { - "title": "Message Collection" - } - // OR - { - "title": "Webhook Call Collection" - } - ``` - - **Invalid Example** - - ```json - { - "title": "Message List Response" - } - // OR - { - "title": "Webhook Call List" - } - ``` - message: List operation response schema must have title ending with "Collection". -overrides: - - files: - - "**#/paths/~1auth~1verify/get" - rules: - required-list-pagination-request-query-parameters: "off" - - files: - - "**#/paths/~1auth~1verify/get/operationId" - rules: - operation-operationId-format: "off" - - files: - - "**#/paths/~1auth~1verify/get/x-sdkOperation" - rules: - operation-sdkOperation-format: "off" diff --git a/openapi.yaml b/openapi.yaml index 89d2442..cc1d995 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -529,11 +529,11 @@ paths: In this example we're utilizing the `@@web_link@@` tag that generates an unsubscribe link for the recipients. The `web` payload is required when using the `@@web_link@@` value in the `email`.`optOutOption`. - + The `rmsUnsubscribeUrl` is a URL link to the web channel unsubscribe content for `GET` requests, and for one-click unsubscribe functionality via `POST` requests. The `oneClickApiMapping` property requires you to have an existing API Mapping and have access to its details. This API Mapping is triggered for one-click unsubscribe functionality. - + Both the `rmsUnsubscribeUrl` and `oneClickApiMapping` properties are optional, but `rmsUnsubscribeUrl` is required to enable unsubscribe, `oneClickApiMapping` is required to enable one-click unsubscribe. operationId: messageCreate parameters: @@ -11062,12 +11062,41 @@ components: type: object x-tags: - Messages - description: |- + description: | ### Overview Customers can send templated WhatsApp messages via Whispir's API. Templates must first be submitted for approval by WhatsApp in your WhatsApp Business Manager account before any template messages can be sent from Whispir. Please reach out to your Whispir account manager to help you get started. + + + #### WhatsApp Messages via Rich Message Templates + Note that from **October 2025** WhatsApp messages can also be configured in Whispir Rich Message Templates and used to send via the Whispir API! + Please contact your Whispir account manager to see how you can have this feature enabled. + + ##### Examples + Sending via Rich Message Templates is shown in the following examples: + * _WhatsApp Message via WhatsApp Enabled Rich Message Template_ + * _WhatsApp Bulk Message via Rich Message Template_ + * _WhatsApp Message via Rich Message Template with Field Overrides_ + + Customers who have this feature enabled will also have access to using the new _parameters_ object, shown in the following examples: + * _WhatsApp Message with Throttling, Alias, Web Link and Body Positional Parameters_ + * _WhatsApp Bulk Message with Web Link and Body Positional Parameters_ + + **Overriding fields from Rich Message Templates (RMT)** + + Customers have the ability to provide the body positional parameters, alias etc. as part of a Rich Message Template (RMT). + + By default, when sending via our API with a given RMT, all values will be collected from the provided RMT. (See _WhatsApp Message via WhatsApp Enabled Rich Message Template_ example) + + However, these values can also be overriden via the API when sending via the Rich Message Template (see _WhatsApp Message via Rich Message Template with Field Overrides_ example). + + In that sample payload, "subject", "features", and "whatsapp" fields will override what is defined in the provided Rich Message Template. + + For clarity and simplicity, however, it is recommended to choose to send your WhatsApp message either via: + * a WhatsApp enabled Rich Message Template previously defined in our portal + * or providing a WhatsApp object in the payload with all the required details x-examples: WhatsApp Message Using an image: to: '61430933333' @@ -11200,6 +11229,69 @@ components: - text: 'https://hsbc.com/renewal' - text: 'https://hsbc.com/terms' - text: 'https://hsbc.com/privacy' + 'WhatsApp Message with Throttling, Alias, Web Link and Body Positional Parameters': + to: '61417123121,61417123122,61417123123' + subject: WhatsApp Message Subject + body: test WhatsApp @@recipient_first_name@@ + features: + throttlingOptions: + throttling: enabled + configName: testThrottle + aliasOption: + aliasName: OnboardingMessageAlias + web: + body: Test

Test

+ type: text/plain + whatsapp: + type: en + name: onboarding_message + parameters: + - '@@first_name@@' + - '@@date@@' + - '@@sender_full_name@@' + - '@@web_link@@' + WhatsApp Bulk Message with Web Link and Body Positional Parameters: + resource: + resourceId: 422D0A8A4C73BBEA + smsMappingField: mobile + subject: WhatsApp Message Subject + body: test WhatsApp Bulk @@FirstName@@ @@web_link@@ + web: + body: Test

Test

+ type: text/plain + whatsapp: + type: en + name: onboarding_message + parameters: + - '@@firstname@@ and @@date@@' + - '@@sender_full_name@@ and @@web_link@@' + WhatsApp Message via WhatsApp Enabled Rich Message Template: + to: '61417123121,61417123122,61417123123' + messageTemplateName: RMT with WhatsApp Channel Enabled + WhatsApp Bulk Message via Rich Message Template: + resource: + resourceId: 422D0A8A4C73BBEA + smsMappingField: mobile + messageTemplateName: Sample RMT with WhatsApp Channel Enabled + subject: WhatsApp Message Subject + WhatsApp Message via Rich Message Template with Field Overrides: + to: '61417123121,61417123122,61417123123' + messageTemplateName: Sample WhatsApp Enabled Rich Message Template + subject: WhatsApp Message Subject + features: + aliasOption: + aliasName: OverrideAlias + whatsapp: + type: en + name: onboarding_message + parameters: + - '@@first_name@@' + - '@@date@@' + - '@@sender_full_name@@' + x-internal: false + required: + - type + - name properties: type: type: string @@ -11216,12 +11308,24 @@ components: The name of the template to be sent > Must be the same as how it appears in your WhatsApp Business Manager example: account_update_example_image_p + parameters: + type: array + x-stoplight: + id: plfyb1qfo0aup + description: |- + Array of positional parameters that are used to personalise the body of the WhatsApp message. + + For example, + "Hello {{1}}!" + would become + "Hello John!" + if "John" is the first positional parameter + items: + x-stoplight: + id: knri1ijzksqjx + type: string content: $ref: '#/components/schemas/WAContent' - required: - - type - - name - - content WAContent: title: WAContent x-stoplight: @@ -11472,7 +11576,7 @@ components: type: object description: |- The opt-out option payload is optional in line with the Email Distribution List standardization making it a requirement for recipients to opt out of email lists easily, which will add up to a sender's reputation and deliverability. - + The `rmsUnsubscribeUrl` is a URL link to the web channel unsubscribe content for `GET` requests, and for one-click unsubscribe functionality via `POST` requests. This is in line with the [RFC-2369](https://www.ietf.org/rfc/rfc2369.txt) (*The Use of URLs as Meta-Syntax for Core Mail List Commands and their Transport through Message Header Fields*). This will append the `List-Unsubscribe` header in the email message for distribution lists with the URL `rmsUnsubscribeUrl` info as its value. @@ -11480,23 +11584,23 @@ components: The `oneClickApiMapping` property requires you to have an existing API Mapping and have access to its details. This API Mapping is triggered for one-click unsubscribe functionality. This is in line with the [RFC-8058](https://www.ietf.org/rfc/rfc8058.txt) (*Signaling One-Click Functionality for List Email Headers*). This will append the `List-Unsubscribe-Post` header in the email message for distribution lists with the `List-Unsubscribe=One-Click` header as its `POST` endpoint for one-click. - + Both the `rmsUnsubscribeUrl` and `oneClickApiMapping` properties are optional, but `rmsUnsubscribeUrl` is required to enable unsubscribe, `oneClickApiMapping` is required to enable one-click unsubscribe. x-examples: rmsUnsubscribeUrl: '@@web_link@@' - oneClickApiMapping: "2ab64862" + oneClickApiMapping: 2ab64862 x-tags: - Email properties: rmsUnsubscribeUrl: type: string - example: "@@web_link@@#unsubscribe" + example: '@@web_link@@#unsubscribe' description: |- This is a URL link to the web channel unsubscribe content for `GET` requests, and for one-click unsubscribe functionality via `POST` requests. oneClickApiMapping: type: string - example: "2ab64862" + example: 2ab64862 description: |- This is the API mapping code or ID triggered for one-click unsubscribe functionality. This code can be retrieved from the API Mapping page. From 7506a3ac59aad9e9c7ad2a97b2af22096c8ca7de Mon Sep 17 00:00:00 2001 From: "amilisic@whispir.com" Date: Tue, 11 Nov 2025 16:17:50 +1100 Subject: [PATCH 02/11] feat: CORE-2088 Added language field to WhatsApp doc Committed by Whispir-Softwares on behalf of amilisic@whispir.com --- openapi.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/openapi.yaml b/openapi.yaml index cc1d995..c3fa82f 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -11308,6 +11308,15 @@ components: The name of the template to be sent > Must be the same as how it appears in your WhatsApp Business Manager example: account_update_example_image_p + language: + type: string + minLength: 2 + maxLength: 5 + x-stoplight: + id: wxn62vex5hsis + description: |- + The language to use for the WhatsApp Template e.g. "en", "en_AU", "en_SG" etc. + > Must be the same as how it appears in your WhatsApp Business Manager parameters: type: array x-stoplight: From 2dea43038298808b7327dac883a6ed9492782216 Mon Sep 17 00:00:00 2001 From: "amilisic@whispir.com" Date: Tue, 11 Nov 2025 17:38:36 +1100 Subject: [PATCH 03/11] feat: CORE-2088 Minor enhancements to wording of WhatsApp section Committed by Whispir-Softwares on behalf of amilisic@whispir.com --- openapi.yaml | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index c3fa82f..ee4c75b 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -11067,11 +11067,15 @@ components: Customers can send templated WhatsApp messages via Whispir's API. - Templates must first be submitted for approval by WhatsApp in your WhatsApp Business Manager account before any template messages can be sent from Whispir. Please reach out to your Whispir account manager to help you get started. + Templates must first be submitted for approval by WhatsApp in your WhatsApp Business Manager account before any template messages can be sent from Whispir. + + Please reach out to your Whispir account manager to help you get started. + + + #### WhatsApp Messages via Rich Message Templates (V2) + Note that from **October 2025** WhatsApp messages can also be configured in Whispir Rich Message Templates. These templates can then be used to send WhatsApp messages via the Whispir API. - #### WhatsApp Messages via Rich Message Templates - Note that from **October 2025** WhatsApp messages can also be configured in Whispir Rich Message Templates and used to send via the Whispir API! Please contact your Whispir account manager to see how you can have this feature enabled. ##### Examples @@ -11086,15 +11090,15 @@ components: **Overriding fields from Rich Message Templates (RMT)** - Customers have the ability to provide the body positional parameters, alias etc. as part of a Rich Message Template (RMT). + Customers have the ability to configure the body positional parameters, alias etc. as part of a Rich Message Template (RMT). By default, when sending via our API with a given RMT, all values will be collected from the provided RMT. (See _WhatsApp Message via WhatsApp Enabled Rich Message Template_ example) - However, these values can also be overriden via the API when sending via the Rich Message Template (see _WhatsApp Message via Rich Message Template with Field Overrides_ example). + However, the platform allows you to include both the Rich Message Template reference **and** a WhatsApp object in the API payload. - In that sample payload, "subject", "features", and "whatsapp" fields will override what is defined in the provided Rich Message Template. + Please note that in this case the platform will give priority to the message configuration in the **WhatsApp object** over the Rich Message Template. (see _WhatsApp Message via Rich Message Template with Field Overrides_ example). - For clarity and simplicity, however, it is recommended to choose to send your WhatsApp message either via: + For clarity and simplicity, it is recommended to choose to send your WhatsApp message via one of: * a WhatsApp enabled Rich Message Template previously defined in our portal * or providing a WhatsApp object in the payload with all the required details x-examples: @@ -11229,7 +11233,7 @@ components: - text: 'https://hsbc.com/renewal' - text: 'https://hsbc.com/terms' - text: 'https://hsbc.com/privacy' - 'WhatsApp Message with Throttling, Alias, Web Link and Body Positional Parameters': + '(V2) WhatsApp Message with Throttling, Alias, Web Link and Body Positional Parameters': to: '61417123121,61417123122,61417123123' subject: WhatsApp Message Subject body: test WhatsApp @@recipient_first_name@@ @@ -11245,12 +11249,13 @@ components: whatsapp: type: en name: onboarding_message + language: en parameters: - '@@first_name@@' - '@@date@@' - '@@sender_full_name@@' - '@@web_link@@' - WhatsApp Bulk Message with Web Link and Body Positional Parameters: + (V2) WhatsApp Bulk Message with Web Link and Body Positional Parameters: resource: resourceId: 422D0A8A4C73BBEA smsMappingField: mobile @@ -11265,16 +11270,16 @@ components: parameters: - '@@firstname@@ and @@date@@' - '@@sender_full_name@@ and @@web_link@@' - WhatsApp Message via WhatsApp Enabled Rich Message Template: + (V2) WhatsApp Message via WhatsApp Enabled Rich Message Template: to: '61417123121,61417123122,61417123123' messageTemplateName: RMT with WhatsApp Channel Enabled - WhatsApp Bulk Message via Rich Message Template: + (V2) WhatsApp Bulk Message via Rich Message Template: resource: resourceId: 422D0A8A4C73BBEA smsMappingField: mobile messageTemplateName: Sample RMT with WhatsApp Channel Enabled subject: WhatsApp Message Subject - WhatsApp Message via Rich Message Template with Field Overrides: + (V2) WhatsApp Message via Rich Message Template with Field Overrides: to: '61417123121,61417123122,61417123123' messageTemplateName: Sample WhatsApp Enabled Rich Message Template subject: WhatsApp Message Subject @@ -11284,6 +11289,7 @@ components: whatsapp: type: en name: onboarding_message + language: en_SG parameters: - '@@first_name@@' - '@@date@@' @@ -11301,12 +11307,16 @@ components: The type of WhatsApp message to be sent, e.g. "template". Whispir currently only supports type "template". + + **Versions Supported:** *V1*, *V2* example: template name: type: string description: |- The name of the template to be sent > Must be the same as how it appears in your WhatsApp Business Manager + + **Versions Supported:** *V1*, *V2* example: account_update_example_image_p language: type: string @@ -11315,8 +11325,10 @@ components: x-stoplight: id: wxn62vex5hsis description: |- - The language to use for the WhatsApp Template e.g. "en", "en_AU", "en_SG" etc. + The language to use for your WhatsApp Template e.g. "en", "en_AU", "en_SG" etc. > Must be the same as how it appears in your WhatsApp Business Manager + + **Versions Supported:** *V2* parameters: type: array x-stoplight: @@ -11329,6 +11341,8 @@ components: would become "Hello John!" if "John" is the first positional parameter + + **Versions Supported:** *V1*, *V2* items: x-stoplight: id: knri1ijzksqjx From 279f2f5ea943bf3f510aab2b59dd01bae064d787 Mon Sep 17 00:00:00 2001 From: "amilisic@whispir.com" Date: Fri, 14 Nov 2025 16:51:52 +1100 Subject: [PATCH 04/11] feat: CORE-2088 A few wording changes Committed by Whispir-Softwares on behalf of amilisic@whispir.com --- openapi.yaml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index ee4c75b..1e5f19d 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -11072,7 +11072,7 @@ components: Please reach out to your Whispir account manager to help you get started. - #### WhatsApp Messages via Rich Message Templates (V2) + #### WhatsApp Messages via Rich Message Templates (v2) Note that from **October 2025** WhatsApp messages can also be configured in Whispir Rich Message Templates. These templates can then be used to send WhatsApp messages via the Whispir API. @@ -11101,6 +11101,10 @@ components: For clarity and simplicity, it is recommended to choose to send your WhatsApp message via one of: * a WhatsApp enabled Rich Message Template previously defined in our portal * or providing a WhatsApp object in the payload with all the required details + + **v2 compatibility** + + Please note that our WhatsApp v2 version only supports the use of positional body parameters (via the new _parameters_ object). The _content_ object is not supported. x-examples: WhatsApp Message Using an image: to: '61430933333' @@ -11303,20 +11307,16 @@ components: type: string enum: - template - description: |- + description: | The type of WhatsApp message to be sent, e.g. "template". Whispir currently only supports type "template". - - **Versions Supported:** *V1*, *V2* example: template name: type: string - description: |- + description: | The name of the template to be sent > Must be the same as how it appears in your WhatsApp Business Manager - - **Versions Supported:** *V1*, *V2* example: account_update_example_image_p language: type: string @@ -11328,7 +11328,7 @@ components: The language to use for your WhatsApp Template e.g. "en", "en_AU", "en_SG" etc. > Must be the same as how it appears in your WhatsApp Business Manager - **Versions Supported:** *V2* + > Supported in v2 **only** parameters: type: array x-stoplight: @@ -11342,7 +11342,7 @@ components: "Hello John!" if "John" is the first positional parameter - **Versions Supported:** *V1*, *V2* + > Supported in v2 **only** items: x-stoplight: id: knri1ijzksqjx @@ -11399,6 +11399,7 @@ components: - index: '1' sub_type: quick_reply payload: No-Button-Payload + x-internal: false WAContentHeader: title: WAContentHeader x-stoplight: From 76aba53ed72c13728be82d79e1c33b5f370a7639 Mon Sep 17 00:00:00 2001 From: "amilisic@whispir.com" Date: Fri, 14 Nov 2025 17:05:36 +1100 Subject: [PATCH 05/11] feat: CORE-2088 Added .spectral.yaml Committed by Whispir-Softwares on behalf of amilisic@whispir.com --- .spectral.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .spectral.yaml diff --git a/.spectral.yaml b/.spectral.yaml new file mode 100644 index 0000000..e69de29 From af970d8c3fbe2a388c7447bf2df8794465718f35 Mon Sep 17 00:00:00 2001 From: "amilisic@whispir.com" Date: Fri, 14 Nov 2025 17:10:36 +1100 Subject: [PATCH 06/11] feat: CORE-2088 Modified .spectral.yaml Committed by Whispir-Softwares on behalf of amilisic@whispir.com --- .spectral.yaml | 451 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 451 insertions(+) diff --git a/.spectral.yaml b/.spectral.yaml index e69de29..d5bed2a 100644 --- a/.spectral.yaml +++ b/.spectral.yaml @@ -0,0 +1,451 @@ +extends: spectral:oas + +functions: + - oasOpIdFormat + - oasOpSdk + +aliases: + Operation_List_Schema: + description: The schema of all operations that list resources, e.g. GET /resources + targets: + - formats: + - oas3 + given: + - "#Operation_List.responses[?(@property >= 200 && @property < 300)].content[*].schema" + Operation_List: + description: All operations that list resources, e.g. `GET /resources` + targets: + - formats: + - oas3 + given: + - "$.paths[?(!(@property).endsWith('}'))].get" + +rules: + openapi-tags-alphabetical: off + operation-success-response: error + oas2-operation-formData-consume-check: error + operation-parameters: error + operation-tag-defined: error + contact-properties: error + duplicated-entry-in-enum: error + info-contact: error + info-description: error + info-license: error + license-url: error + no-eval-in-markdown: error + no-script-tags-in-markdown: error + openapi-tags: error + operation-description: error + operation-operationId: error + operation-operationId-valid-in-url: error + operation-singular-tag: error + operation-tags: error + path-declarations-must-exist: error + path-keys-no-trailing-slash: error + path-not-include-query: error + tag-description: error + typed-enum: error + oas2-api-host: error + oas2-api-schemes: error + oas2-host-not-example: error + oas2-host-trailing-slash: error + oas2-parameter-description: error + oas2-operation-security-defined: error + oas2-anyOf: error + oas2-oneOf: error + oas2-unused-definition: error + oas3-api-servers: error + oas3-examples-value-or-externalValue: error + oas3-operation-security-defined: error + oas3-parameter-description: error + oas3-server-not-example.com: error + oas3-server-trailing-slash: error + oas3-unused-component: error + + ### CORE RULES ### + schema-examples: + description: Schemas must have non-empty "x-examples" object. + severity: error + given: $.components.schemas[*] + then: + field: x-examples + function: schema + functionOptions: + dialect: draft7 + schema: + type: object + minProperties: 1 + schema-descriptions: + description: Schemas must have a "description". + severity: error + given: + - $..schemas[*] + - $..responses[*].content[*]..schema + - $..requestBody.content[*]..schema + then: + field: description + function: truthy + schema-property-descriptions: # Note: this rule is not de-duplicated, and will show on parents $ref'ing children with issues until all child issues are resolved. + description: Schema properties must have a "description". + severity: error + given: + - $..schema[*]..properties[*] + - $..responses[*].content[*]..schema..properties[*] + - $..requestBody.content[*]..schema..properties[*] + - $..headers[*] + then: + field: description + function: truthy + schema-property-examples: # Note: this rule is not de-duplicated, and will show on parents $ref'ing children with issues until all child issues are resolved. + description: Schema properties must have an "example". + severity: error + given: + - $..schema[*]..properties[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] + - $..responses[*].content[*]..schema..properties[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] + - $..requestBody.content[*]..schema..properties[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] + - $..headers[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] + then: + field: example + function: defined + payload-examples: + description: Request & response payload content must have non-empty "examples" object. + severity: error + given: + - $..responses[*].content[*] + - $..requestBody.content[*] + then: + field: examples + function: schema + functionOptions: + dialect: draft7 + schema: + type: object + minProperties: 1 + + ### API SPECIFIC RULES ### + required-all-request-headers: + given: + - "$.paths[*][get,put,post,delete,options,head,patch,trace]" + severity: error + then: + function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^X-Api-Key$" + field: parameters + description: 'All requests must have the following headers "parameters": "X-Api-Key".' + required-read-request-headers: + given: + - "$.paths[*][get]" + severity: error + then: + function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^Accept$" + field: parameters + description: 'Read requests must have the following headers "parameters": "Accept".' + required-write-request-headers: + given: + - "$.paths[*][put,post]" + severity: error + then: + - function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^Content-Type$" + field: parameters + - function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^Accept$" + field: parameters + description: + 'Write requests must have the following headers "parameters": "Content-Type", + "Accept".' + required-list-pagination-request-query-parameters: + given: + - "$.paths[?(!/{.*Id}$/i.test(@property))]['get']" + severity: error + then: + - function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^limit$" + field: parameters + - function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^offset$" + field: parameters + - function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^sortOrder$" + field: parameters + - function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^sortFields$" + field: parameters + description: + 'Read list requests must have the following query "parameters": "limit", + "offset", "sortOrder", "sortFields".' + required-all-response-headers: + given: + - "$..responses[*]" + severity: error + then: + function: schema + functionOptions: + dialect: draft7 + schema: + type: object + required: + - Access-Control-Allow-Origin + - Expires + - Cache-Control + field: headers + description: + 'All responses must have the following "headers": "Access-Control-Allow-Origin", + "Expires", "Cache-Control".' + required-content-response-headers: + given: + - "$.paths[*]..responses[?(@property != 204)]" + severity: error + then: + function: schema + functionOptions: + dialect: draft7 + schema: + type: object + required: + - Content-Type + - Content-Length + field: headers + description: + 'Responses with content must have the following "headers": "Content-Type", + "Content-Length".' + required-post-response-headers: + given: + - "$.paths[*][post]..responses[?(@property != 204 && @property >= 200 && @property + < 300)]" + severity: error + then: + function: schema + functionOptions: + dialect: draft7 + schema: + type: object + required: + - Location + field: headers + description: + 'Successful post endpoint responses must have the following "headers": + "Location".' + operation-sdkOperation-format: + given: + - "$" + severity: error + then: + function: oasOpSdk + functionOptions: + exceptions: [] + description: |- + All operations must include an "x-sdkOperation" vendor extension for use in human-readable SDK method names. + + Must start with one of the following values, corresponding to the operation on the resource: + * `create` - `POST /resource` + * `retrieve` - `GET /resource/{resourceId}` + * `update` - `PUT /resource/{resourceId}` + * `delete` - `DELETE /resource/{resourceId}` + * `list` - `GET /resource` + + An optional noun suffix describing any sub-resources may be included as appropriate. + + **Valid Example** + + ```json + { + "x-sdkOperation": "create" + } + // OR + { + "x-sdkOperation": "listWebhookCalls" + } + ``` + + **Invalid Example** + + ```json + { + "x-sdkOperation": "post" + } + // OR + { + "x-sdkOperation": "getWebhookCalls" + } + ``` + message: "{{error}}" + operation-operationId-format: + given: + - "$" + severity: error + then: + function: oasOpIdFormat + functionOptions: + exceptions: [] + description: |- + All operations must include an appropriately named "operationId". + + Must end with one of the following values, corresponding to the operation on the + resource: + * `Create` - `POST /resource` + * `Retrieve` - `GET /resource/{resourceId}` + * `Update` - `PUT /resource/{resourceId}` + * `Delete` - `DELETE /resource/{resourceId}` + * `List` - `GET /resource` + + An optional noun prefix describing any sub-resources + may be included as appropriate. + + **Valid Example** + + ```json + { + "operationId": "messageCreate" + } + // OR + { + "operationId": "webhookCallList" + } + ``` + + **Invalid Example** + + ```json + { + "operationId": "getMessage" + } + // OR + { + "operationId": "getWebhookCalls" + } + ``` + message: "{{error}}" + operation-list-schema-keys: + given: + - "#Operation_List_Schema" + severity: error + then: + function: pattern + functionOptions: + match: Collection$ + field: "$ref" + description: List operation response schema must have key ending with "Collection". + message: List operation response schema must have key ending with "Collection". + operation-list-schema-title: + given: + - "#Operation_List_Schema" + severity: error + then: + function: pattern + functionOptions: + match: Collection$ + field: title + description: |- + List operation response schema must have title ending with "Collection". + + **Valid Example** + + ```json + { + "title": "Message Collection" + } + // OR + { + "title": "Webhook Call Collection" + } + ``` + + **Invalid Example** + + ```json + { + "title": "Message List Response" + } + // OR + { + "title": "Webhook Call List" + } + ``` + message: List operation response schema must have title ending with "Collection". +overrides: + - files: + - "**#/paths/~1auth~1verify/get" + rules: + required-list-pagination-request-query-parameters: "off" + - files: + - "**#/paths/~1auth~1verify/get/operationId" + rules: + operation-operationId-format: "off" + - files: + - "**#/paths/~1auth~1verify/get/x-sdkOperation" + rules: + operation-sdkOperation-format: "off" From ef35b2a2d3137eb9830e37ae729d43d02c09ee03 Mon Sep 17 00:00:00 2001 From: "amilisic@whispir.com" Date: Fri, 14 Nov 2025 17:15:47 +1100 Subject: [PATCH 07/11] feat: CORE-2088 Deleted .spectral.yaml Committed by Whispir-Softwares on behalf of amilisic@whispir.com --- .spectral.yaml | 451 ------------------------------------------------- 1 file changed, 451 deletions(-) delete mode 100644 .spectral.yaml diff --git a/.spectral.yaml b/.spectral.yaml deleted file mode 100644 index d5bed2a..0000000 --- a/.spectral.yaml +++ /dev/null @@ -1,451 +0,0 @@ -extends: spectral:oas - -functions: - - oasOpIdFormat - - oasOpSdk - -aliases: - Operation_List_Schema: - description: The schema of all operations that list resources, e.g. GET /resources - targets: - - formats: - - oas3 - given: - - "#Operation_List.responses[?(@property >= 200 && @property < 300)].content[*].schema" - Operation_List: - description: All operations that list resources, e.g. `GET /resources` - targets: - - formats: - - oas3 - given: - - "$.paths[?(!(@property).endsWith('}'))].get" - -rules: - openapi-tags-alphabetical: off - operation-success-response: error - oas2-operation-formData-consume-check: error - operation-parameters: error - operation-tag-defined: error - contact-properties: error - duplicated-entry-in-enum: error - info-contact: error - info-description: error - info-license: error - license-url: error - no-eval-in-markdown: error - no-script-tags-in-markdown: error - openapi-tags: error - operation-description: error - operation-operationId: error - operation-operationId-valid-in-url: error - operation-singular-tag: error - operation-tags: error - path-declarations-must-exist: error - path-keys-no-trailing-slash: error - path-not-include-query: error - tag-description: error - typed-enum: error - oas2-api-host: error - oas2-api-schemes: error - oas2-host-not-example: error - oas2-host-trailing-slash: error - oas2-parameter-description: error - oas2-operation-security-defined: error - oas2-anyOf: error - oas2-oneOf: error - oas2-unused-definition: error - oas3-api-servers: error - oas3-examples-value-or-externalValue: error - oas3-operation-security-defined: error - oas3-parameter-description: error - oas3-server-not-example.com: error - oas3-server-trailing-slash: error - oas3-unused-component: error - - ### CORE RULES ### - schema-examples: - description: Schemas must have non-empty "x-examples" object. - severity: error - given: $.components.schemas[*] - then: - field: x-examples - function: schema - functionOptions: - dialect: draft7 - schema: - type: object - minProperties: 1 - schema-descriptions: - description: Schemas must have a "description". - severity: error - given: - - $..schemas[*] - - $..responses[*].content[*]..schema - - $..requestBody.content[*]..schema - then: - field: description - function: truthy - schema-property-descriptions: # Note: this rule is not de-duplicated, and will show on parents $ref'ing children with issues until all child issues are resolved. - description: Schema properties must have a "description". - severity: error - given: - - $..schema[*]..properties[*] - - $..responses[*].content[*]..schema..properties[*] - - $..requestBody.content[*]..schema..properties[*] - - $..headers[*] - then: - field: description - function: truthy - schema-property-examples: # Note: this rule is not de-duplicated, and will show on parents $ref'ing children with issues until all child issues are resolved. - description: Schema properties must have an "example". - severity: error - given: - - $..schema[*]..properties[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] - - $..responses[*].content[*]..schema..properties[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] - - $..requestBody.content[*]..schema..properties[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] - - $..headers[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] - then: - field: example - function: defined - payload-examples: - description: Request & response payload content must have non-empty "examples" object. - severity: error - given: - - $..responses[*].content[*] - - $..requestBody.content[*] - then: - field: examples - function: schema - functionOptions: - dialect: draft7 - schema: - type: object - minProperties: 1 - - ### API SPECIFIC RULES ### - required-all-request-headers: - given: - - "$.paths[*][get,put,post,delete,options,head,patch,trace]" - severity: error - then: - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^X-Api-Key$" - field: parameters - description: 'All requests must have the following headers "parameters": "X-Api-Key".' - required-read-request-headers: - given: - - "$.paths[*][get]" - severity: error - then: - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^Accept$" - field: parameters - description: 'Read requests must have the following headers "parameters": "Accept".' - required-write-request-headers: - given: - - "$.paths[*][put,post]" - severity: error - then: - - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^Content-Type$" - field: parameters - - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^Accept$" - field: parameters - description: - 'Write requests must have the following headers "parameters": "Content-Type", - "Accept".' - required-list-pagination-request-query-parameters: - given: - - "$.paths[?(!/{.*Id}$/i.test(@property))]['get']" - severity: error - then: - - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^limit$" - field: parameters - - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^offset$" - field: parameters - - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^sortOrder$" - field: parameters - - function: schema - functionOptions: - dialect: draft7 - schema: - type: array - contains: - type: object - properties: - name: - type: string - pattern: "^sortFields$" - field: parameters - description: - 'Read list requests must have the following query "parameters": "limit", - "offset", "sortOrder", "sortFields".' - required-all-response-headers: - given: - - "$..responses[*]" - severity: error - then: - function: schema - functionOptions: - dialect: draft7 - schema: - type: object - required: - - Access-Control-Allow-Origin - - Expires - - Cache-Control - field: headers - description: - 'All responses must have the following "headers": "Access-Control-Allow-Origin", - "Expires", "Cache-Control".' - required-content-response-headers: - given: - - "$.paths[*]..responses[?(@property != 204)]" - severity: error - then: - function: schema - functionOptions: - dialect: draft7 - schema: - type: object - required: - - Content-Type - - Content-Length - field: headers - description: - 'Responses with content must have the following "headers": "Content-Type", - "Content-Length".' - required-post-response-headers: - given: - - "$.paths[*][post]..responses[?(@property != 204 && @property >= 200 && @property - < 300)]" - severity: error - then: - function: schema - functionOptions: - dialect: draft7 - schema: - type: object - required: - - Location - field: headers - description: - 'Successful post endpoint responses must have the following "headers": - "Location".' - operation-sdkOperation-format: - given: - - "$" - severity: error - then: - function: oasOpSdk - functionOptions: - exceptions: [] - description: |- - All operations must include an "x-sdkOperation" vendor extension for use in human-readable SDK method names. - - Must start with one of the following values, corresponding to the operation on the resource: - * `create` - `POST /resource` - * `retrieve` - `GET /resource/{resourceId}` - * `update` - `PUT /resource/{resourceId}` - * `delete` - `DELETE /resource/{resourceId}` - * `list` - `GET /resource` - - An optional noun suffix describing any sub-resources may be included as appropriate. - - **Valid Example** - - ```json - { - "x-sdkOperation": "create" - } - // OR - { - "x-sdkOperation": "listWebhookCalls" - } - ``` - - **Invalid Example** - - ```json - { - "x-sdkOperation": "post" - } - // OR - { - "x-sdkOperation": "getWebhookCalls" - } - ``` - message: "{{error}}" - operation-operationId-format: - given: - - "$" - severity: error - then: - function: oasOpIdFormat - functionOptions: - exceptions: [] - description: |- - All operations must include an appropriately named "operationId". - - Must end with one of the following values, corresponding to the operation on the - resource: - * `Create` - `POST /resource` - * `Retrieve` - `GET /resource/{resourceId}` - * `Update` - `PUT /resource/{resourceId}` - * `Delete` - `DELETE /resource/{resourceId}` - * `List` - `GET /resource` - - An optional noun prefix describing any sub-resources - may be included as appropriate. - - **Valid Example** - - ```json - { - "operationId": "messageCreate" - } - // OR - { - "operationId": "webhookCallList" - } - ``` - - **Invalid Example** - - ```json - { - "operationId": "getMessage" - } - // OR - { - "operationId": "getWebhookCalls" - } - ``` - message: "{{error}}" - operation-list-schema-keys: - given: - - "#Operation_List_Schema" - severity: error - then: - function: pattern - functionOptions: - match: Collection$ - field: "$ref" - description: List operation response schema must have key ending with "Collection". - message: List operation response schema must have key ending with "Collection". - operation-list-schema-title: - given: - - "#Operation_List_Schema" - severity: error - then: - function: pattern - functionOptions: - match: Collection$ - field: title - description: |- - List operation response schema must have title ending with "Collection". - - **Valid Example** - - ```json - { - "title": "Message Collection" - } - // OR - { - "title": "Webhook Call Collection" - } - ``` - - **Invalid Example** - - ```json - { - "title": "Message List Response" - } - // OR - { - "title": "Webhook Call List" - } - ``` - message: List operation response schema must have title ending with "Collection". -overrides: - - files: - - "**#/paths/~1auth~1verify/get" - rules: - required-list-pagination-request-query-parameters: "off" - - files: - - "**#/paths/~1auth~1verify/get/operationId" - rules: - operation-operationId-format: "off" - - files: - - "**#/paths/~1auth~1verify/get/x-sdkOperation" - rules: - operation-sdkOperation-format: "off" From 536036ccc1dce81809b23ef6aabdbfda5a955957 Mon Sep 17 00:00:00 2001 From: "amilisic@whispir.com" Date: Fri, 14 Nov 2025 17:18:20 +1100 Subject: [PATCH 08/11] feat: CORE-2088 Added .stoplight/.spectral.yml_original Committed by Whispir-Softwares on behalf of amilisic@whispir.com --- .stoplight/.spectral.yml_original | 451 ++++++++++++++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 .stoplight/.spectral.yml_original diff --git a/.stoplight/.spectral.yml_original b/.stoplight/.spectral.yml_original new file mode 100644 index 0000000..d5bed2a --- /dev/null +++ b/.stoplight/.spectral.yml_original @@ -0,0 +1,451 @@ +extends: spectral:oas + +functions: + - oasOpIdFormat + - oasOpSdk + +aliases: + Operation_List_Schema: + description: The schema of all operations that list resources, e.g. GET /resources + targets: + - formats: + - oas3 + given: + - "#Operation_List.responses[?(@property >= 200 && @property < 300)].content[*].schema" + Operation_List: + description: All operations that list resources, e.g. `GET /resources` + targets: + - formats: + - oas3 + given: + - "$.paths[?(!(@property).endsWith('}'))].get" + +rules: + openapi-tags-alphabetical: off + operation-success-response: error + oas2-operation-formData-consume-check: error + operation-parameters: error + operation-tag-defined: error + contact-properties: error + duplicated-entry-in-enum: error + info-contact: error + info-description: error + info-license: error + license-url: error + no-eval-in-markdown: error + no-script-tags-in-markdown: error + openapi-tags: error + operation-description: error + operation-operationId: error + operation-operationId-valid-in-url: error + operation-singular-tag: error + operation-tags: error + path-declarations-must-exist: error + path-keys-no-trailing-slash: error + path-not-include-query: error + tag-description: error + typed-enum: error + oas2-api-host: error + oas2-api-schemes: error + oas2-host-not-example: error + oas2-host-trailing-slash: error + oas2-parameter-description: error + oas2-operation-security-defined: error + oas2-anyOf: error + oas2-oneOf: error + oas2-unused-definition: error + oas3-api-servers: error + oas3-examples-value-or-externalValue: error + oas3-operation-security-defined: error + oas3-parameter-description: error + oas3-server-not-example.com: error + oas3-server-trailing-slash: error + oas3-unused-component: error + + ### CORE RULES ### + schema-examples: + description: Schemas must have non-empty "x-examples" object. + severity: error + given: $.components.schemas[*] + then: + field: x-examples + function: schema + functionOptions: + dialect: draft7 + schema: + type: object + minProperties: 1 + schema-descriptions: + description: Schemas must have a "description". + severity: error + given: + - $..schemas[*] + - $..responses[*].content[*]..schema + - $..requestBody.content[*]..schema + then: + field: description + function: truthy + schema-property-descriptions: # Note: this rule is not de-duplicated, and will show on parents $ref'ing children with issues until all child issues are resolved. + description: Schema properties must have a "description". + severity: error + given: + - $..schema[*]..properties[*] + - $..responses[*].content[*]..schema..properties[*] + - $..requestBody.content[*]..schema..properties[*] + - $..headers[*] + then: + field: description + function: truthy + schema-property-examples: # Note: this rule is not de-duplicated, and will show on parents $ref'ing children with issues until all child issues are resolved. + description: Schema properties must have an "example". + severity: error + given: + - $..schema[*]..properties[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] + - $..responses[*].content[*]..schema..properties[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] + - $..requestBody.content[*]..schema..properties[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] + - $..headers[?(@ && @.type && @.type !== "array" && @.type !== "object" && @.type !== "boolean" && !@.enum)] + then: + field: example + function: defined + payload-examples: + description: Request & response payload content must have non-empty "examples" object. + severity: error + given: + - $..responses[*].content[*] + - $..requestBody.content[*] + then: + field: examples + function: schema + functionOptions: + dialect: draft7 + schema: + type: object + minProperties: 1 + + ### API SPECIFIC RULES ### + required-all-request-headers: + given: + - "$.paths[*][get,put,post,delete,options,head,patch,trace]" + severity: error + then: + function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^X-Api-Key$" + field: parameters + description: 'All requests must have the following headers "parameters": "X-Api-Key".' + required-read-request-headers: + given: + - "$.paths[*][get]" + severity: error + then: + function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^Accept$" + field: parameters + description: 'Read requests must have the following headers "parameters": "Accept".' + required-write-request-headers: + given: + - "$.paths[*][put,post]" + severity: error + then: + - function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^Content-Type$" + field: parameters + - function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^Accept$" + field: parameters + description: + 'Write requests must have the following headers "parameters": "Content-Type", + "Accept".' + required-list-pagination-request-query-parameters: + given: + - "$.paths[?(!/{.*Id}$/i.test(@property))]['get']" + severity: error + then: + - function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^limit$" + field: parameters + - function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^offset$" + field: parameters + - function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^sortOrder$" + field: parameters + - function: schema + functionOptions: + dialect: draft7 + schema: + type: array + contains: + type: object + properties: + name: + type: string + pattern: "^sortFields$" + field: parameters + description: + 'Read list requests must have the following query "parameters": "limit", + "offset", "sortOrder", "sortFields".' + required-all-response-headers: + given: + - "$..responses[*]" + severity: error + then: + function: schema + functionOptions: + dialect: draft7 + schema: + type: object + required: + - Access-Control-Allow-Origin + - Expires + - Cache-Control + field: headers + description: + 'All responses must have the following "headers": "Access-Control-Allow-Origin", + "Expires", "Cache-Control".' + required-content-response-headers: + given: + - "$.paths[*]..responses[?(@property != 204)]" + severity: error + then: + function: schema + functionOptions: + dialect: draft7 + schema: + type: object + required: + - Content-Type + - Content-Length + field: headers + description: + 'Responses with content must have the following "headers": "Content-Type", + "Content-Length".' + required-post-response-headers: + given: + - "$.paths[*][post]..responses[?(@property != 204 && @property >= 200 && @property + < 300)]" + severity: error + then: + function: schema + functionOptions: + dialect: draft7 + schema: + type: object + required: + - Location + field: headers + description: + 'Successful post endpoint responses must have the following "headers": + "Location".' + operation-sdkOperation-format: + given: + - "$" + severity: error + then: + function: oasOpSdk + functionOptions: + exceptions: [] + description: |- + All operations must include an "x-sdkOperation" vendor extension for use in human-readable SDK method names. + + Must start with one of the following values, corresponding to the operation on the resource: + * `create` - `POST /resource` + * `retrieve` - `GET /resource/{resourceId}` + * `update` - `PUT /resource/{resourceId}` + * `delete` - `DELETE /resource/{resourceId}` + * `list` - `GET /resource` + + An optional noun suffix describing any sub-resources may be included as appropriate. + + **Valid Example** + + ```json + { + "x-sdkOperation": "create" + } + // OR + { + "x-sdkOperation": "listWebhookCalls" + } + ``` + + **Invalid Example** + + ```json + { + "x-sdkOperation": "post" + } + // OR + { + "x-sdkOperation": "getWebhookCalls" + } + ``` + message: "{{error}}" + operation-operationId-format: + given: + - "$" + severity: error + then: + function: oasOpIdFormat + functionOptions: + exceptions: [] + description: |- + All operations must include an appropriately named "operationId". + + Must end with one of the following values, corresponding to the operation on the + resource: + * `Create` - `POST /resource` + * `Retrieve` - `GET /resource/{resourceId}` + * `Update` - `PUT /resource/{resourceId}` + * `Delete` - `DELETE /resource/{resourceId}` + * `List` - `GET /resource` + + An optional noun prefix describing any sub-resources + may be included as appropriate. + + **Valid Example** + + ```json + { + "operationId": "messageCreate" + } + // OR + { + "operationId": "webhookCallList" + } + ``` + + **Invalid Example** + + ```json + { + "operationId": "getMessage" + } + // OR + { + "operationId": "getWebhookCalls" + } + ``` + message: "{{error}}" + operation-list-schema-keys: + given: + - "#Operation_List_Schema" + severity: error + then: + function: pattern + functionOptions: + match: Collection$ + field: "$ref" + description: List operation response schema must have key ending with "Collection". + message: List operation response schema must have key ending with "Collection". + operation-list-schema-title: + given: + - "#Operation_List_Schema" + severity: error + then: + function: pattern + functionOptions: + match: Collection$ + field: title + description: |- + List operation response schema must have title ending with "Collection". + + **Valid Example** + + ```json + { + "title": "Message Collection" + } + // OR + { + "title": "Webhook Call Collection" + } + ``` + + **Invalid Example** + + ```json + { + "title": "Message List Response" + } + // OR + { + "title": "Webhook Call List" + } + ``` + message: List operation response schema must have title ending with "Collection". +overrides: + - files: + - "**#/paths/~1auth~1verify/get" + rules: + required-list-pagination-request-query-parameters: "off" + - files: + - "**#/paths/~1auth~1verify/get/operationId" + rules: + operation-operationId-format: "off" + - files: + - "**#/paths/~1auth~1verify/get/x-sdkOperation" + rules: + operation-sdkOperation-format: "off" From 94abb5f30ff547a44890858488f334151ad1c609 Mon Sep 17 00:00:00 2001 From: "amilisic@whispir.com" Date: Fri, 14 Nov 2025 17:24:12 +1100 Subject: [PATCH 09/11] feat: CORE-2088 Re-added .spectral.yml Committed by Whispir-Softwares on behalf of amilisic@whispir.com --- .spectral.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .spectral.yml diff --git a/.spectral.yml b/.spectral.yml new file mode 100644 index 0000000..d7148d8 --- /dev/null +++ b/.spectral.yml @@ -0,0 +1,4 @@ + +extends: "spectral:oas" # or any preset you were using +rules: + "*": "off" From 06924aed021d28792bd869d202b77243ce556b28 Mon Sep 17 00:00:00 2001 From: "amilisic@whispir.com" Date: Fri, 14 Nov 2025 17:26:18 +1100 Subject: [PATCH 10/11] feat: CORE-2088 Modified .spectral.yml Committed by Whispir-Softwares on behalf of amilisic@whispir.com --- .spectral.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.spectral.yml b/.spectral.yml index d7148d8..77b558e 100644 --- a/.spectral.yml +++ b/.spectral.yml @@ -1,4 +1,4 @@ -extends: "spectral:oas" # or any preset you were using rules: "*": "off" + From 891e2d7a818461a5205abe5b747e64b707a5d74e Mon Sep 17 00:00:00 2001 From: "amilisic@whispir.com" Date: Fri, 14 Nov 2025 17:28:50 +1100 Subject: [PATCH 11/11] feat: CORE-2088 Modified .spectral.yml Committed by Whispir-Softwares on behalf of amilisic@whispir.com --- .spectral.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.spectral.yml b/.spectral.yml index 77b558e..e90b8db 100644 --- a/.spectral.yml +++ b/.spectral.yml @@ -1,4 +1,3 @@ -rules: - "*": "off" +rules: {}