From a46dd21bb80263eed23ccd4c2f029b91e36e09f0 Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Fri, 4 Sep 2026 11:00:02 +0200 Subject: [PATCH 01/11] fix: r4.1 release review findings - #285: clarify mandatory /check and /retrieve-date operations - #286: update sim-swap-subscriptions description for v0.4.0 - #287: fix RFC 3339 link, restore example, fix EventSwapped description - #289: add test definitions for /retrieve-age-band --- .../sim-swap-subscriptions.yaml | 16 +-- code/API_definitions/sim-swap.yaml | 21 +-- .../sim-swap-retrieveSimSwapAgeBand.feature | 132 ++++++++++++++++++ 3 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature diff --git a/code/API_definitions/sim-swap-subscriptions.yaml b/code/API_definitions/sim-swap-subscriptions.yaml index b45f5bd..59ebda0 100644 --- a/code/API_definitions/sim-swap-subscriptions.yaml +++ b/code/API_definitions/sim-swap-subscriptions.yaml @@ -2,7 +2,7 @@ openapi: 3.0.3 info: title: Sim Swap Subscriptions description: | - This API provides the customer with the ability to subscribe to event related to a sim swap operation performed on an associated phone number. + This API provides the customer with the ability to subscribe to events related to a SIM swap operation performed on an associated phone number. # Introduction @@ -16,18 +16,18 @@ info: # API Functionality The API exposes following capabilities: - * **/subscriptions**: This endpoint has two operations, one that allows to retrieve the list of Sim Swap event subscriptions and another one to create new event subscriptions. + * **/subscriptions**: This endpoint has two operations, one that allows to retrieve the list of Sim Swap event subscriptions and another one to create new event subscriptions. The list operation supports pagination via `page` and `perPage` query parameters returning a `SubscriptionList` response. * **/subscriptions/{subscriptionId}**: This endpoint also has two operations, one that allows to retrieve a particular event subscription by id and another one to delete an event subscription also using the id. ## Sim Swap notification subscription - Theses endpoints allow to manage notification subscription on Sim Swap event. - The CAMARA subscription model is detailed in the CAMARA API design guideline document and follows Cloudevents specification. + These endpoints allow to manage notification subscription on Sim Swap event. + The CAMARA subscription model is detailed in the CAMARA API design guideline document and follows Cloudevents specification. Sink credentials support ACCESSTOKEN and PRIVATE_KEY_JWT authentication types. - It is mandatory in the subscription to provide the event `types` subscribed are several are managed in this API. + Since this API manages several event types, it is mandatory to provide the `types` field when creating a subscription. - Only one event ``types`` is managed for this API: - - ``org.camaraproject.sim-swap-subscriptions.v0.swapped`` - Event triggered when a sim swap occurs on the associated phoneNumber + Only one event `types` is managed for this API: + - `org.camaraproject.sim-swap-subscriptions.v0.swapped` - Event triggered when a SIM swap occurs on the associated phone number Note: Additionally to these list, ``org.camaraproject.sim-swap-subscriptions.v0.subscription-ended`` notification `types` is sent when the subscription ends. @@ -586,7 +586,7 @@ components: - org.camaraproject.sim-swap-subscriptions.v0.subscription-ended EventSwapped: - description: event structure for network type change + description: event structure for SIM swap event allOf: - $ref: "#/components/schemas/ApiNotificationEvent" - type: object diff --git a/code/API_definitions/sim-swap.yaml b/code/API_definitions/sim-swap.yaml index bfca8cb..c572f81 100644 --- a/code/API_definitions/sim-swap.yaml +++ b/code/API_definitions/sim-swap.yaml @@ -12,12 +12,13 @@ info: The SIM Swap API can also be used to protect non-automated actions. For example, when a call center expert contacts a user to clarify or confirm a sensitive operation. - This API is used by an application to get information about a mobile line latest SIM swap date. It can be easily integrated and used through this secured API and allows SPs (Service Provider) to get this information an easy & secured way. The API provides management of 3 endpoints answering 3 distinct questions: - An API provider is not expected to support all of them: the age-band operation is an alternative to exposing the exact SIM swap date, and support depends on the provider's available capabilities and commercial use case. + This API is used by an application to get information about a mobile line latest SIM swap date. It can be easily integrated and used through this secured API and allows SPs (Service Provider) to get this information in an easy and secured way. The API provides management of 3 endpoints answering 3 distinct questions: - * When did the last SIM swap occur? - * Has a SIM swap occurred during last n hours? - * How recently did the last SIM swap occur, as a standardized time band? (alternative to the exact date) + * When did the last SIM swap occur? (mandatory `/retrieve-date` operation) + * Has a SIM swap occurred during last n hours? (mandatory `/check` operation) + * How recently did the last SIM swap occur, as a standardized time band? (optional `/retrieve-age-band` operation, an alternative to exposing the exact SIM swap date) + + An API provider is expected to support `/check` and `/retrieve-date`. The `/retrieve-age-band` operation is optional and is an alternative for providers that do not expose the exact SIM swap date; support depends on the provider's available capabilities and commercial use case. A provider that does not implement `/retrieve-age-band` returns `501 NOT_IMPLEMENTED`. # Relevant terms and definitions @@ -269,9 +270,10 @@ paths: occurred, expressed as a time band. This operation is an alternative way to expose SIM swap recency for API providers that - do not expose the exact SIM swap date. It does not return the actual SIM swap date, and - is not expected to be supported in addition to `check` and `retrieve-date`; support - depends on the provider's available capabilities and commercial use case. + do not expose the exact SIM swap date. It does not return the actual SIM swap date and + is not expected to be supported in addition to `check` and `retrieve-date`. Consult your + provider to determine if this optional operation is available; support depends on the provider's + available capabilities and commercial use case. The returned value is a technical network signal indicating recency. Consuming parties apply their own decisioning outside the API contract. Value `111` indicates the provider positively confirms a SIM @@ -348,7 +350,8 @@ components: format: date-time maxLength: 64 nullable: true - description: Timestamp of latest SIM swap performed. It must follow [RFC 3339 (https://datatracker.ietf.org/doc/html/rfc3339#section-5.6) and must have time zone. + description: Timestamp of latest SIM swap performed. It must follow [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6) and must have time zone. The value is `null` when the provider cannot return it for privacy reasons (the `monitoredPeriod` mechanism applies). + example: "2023-07-03T14:27:08.312+02:00" monitoredPeriod: type: integer format: int32 diff --git a/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature b/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature new file mode 100644 index 0000000..f59fafc --- /dev/null +++ b/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature @@ -0,0 +1,132 @@ +Feature: CAMARA SIM Swap API, vwip - Operation retrieveSimSwapAgeBand + + # Input to be provided by the implementation to the tester + # + # Testing assets: + # + # References to OAS spec schemas refer to schemas specified in sim_swap.yaml + # + # Retrieve SIM swap age band + + Background: Common retrieveSimSwapAgeBand setup + Given the resource "/sim-swap/vwip/retrieve-age-band" + And the header "Content-Type" is set to "application/json" + And the header "Authorization" is set to a valid access token + And the header "x-correlator" complies with the schema at "#/components/schemas/XCorrelator" + And the request body is set by default to a request body compliant with the schema + + # This first scenario serves as a minimum, not testing any specific age band value + @retrieve_age_band_1_generic_success_scenario + Scenario: Common validations for any success scenario + Given a valid phone number identified by the token or provided in the request body + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 200 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has same value as the request header "x-correlator" + And the response body complies with the OAS schema at "/components/schemas/SimSwapAgeBandInfo" + + # Scenarios testing specific age band values + + @retrieve_age_band_2_recent_swap_band_1 + Scenario: Retrieve age band showing recent SIM swap (band 1) + Given a valid phone number identified by the token or provided in the request body + And the SIM for this phone number has been swapped in the last 4 hours + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 200 + And the value of response property "$.simSwapAgeBand" == 1 + + @retrieve_age_band_3_mid_age_swap_band_10 + Scenario: Retrieve age band showing mid-age SIM swap (band 10) + Given a valid phone number identified by the token or provided in the request body + And the SIM for this phone number has been swapped in the last 30 days + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 200 + And the value of response property "$.simSwapAgeBand" == 10 + + @retrieve_age_band_4_old_swap_band_17 + Scenario: Retrieve age band showing old SIM swap (band 17) + Given a valid phone number identified by the token or provided in the request body + And the SIM for this phone number has been swapped more than 3 years ago + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 200 + And the value of response property "$.simSwapAgeBand" == 17 + + @retrieve_age_band_5_never_swapped_sentinel_111 + Scenario: Retrieve age band showing SIM has never been swapped (sentinel 111) + Given a valid phone number identified by the token or provided in the request body + And the SIM for this phone number has never been swapped + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 200 + And the value of response property "$.simSwapAgeBand" == 111 + + # Scenarios testing different access token types + + @retrieve_age_band_6_3legged_token + Scenario: Retrieve age band using 3-legged access token + Given the header "Authorization" is set to a valid 3-legged access token identifying a phone number + And the request body does not include "phoneNumber" + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 200 + And the response body complies with the OAS schema at "/components/schemas/SimSwapAgeBandInfo" + + @retrieve_age_band_7_2legged_token + Scenario: Retrieve age band using 2-legged access token + Given the header "Authorization" is set to a valid 2-legged access token + And the request body property "$.phoneNumber" is set to a valid phone number + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 200 + And the response body complies with the OAS schema at "/components/schemas/SimSwapAgeBandInfo" + + # Error scenarios + + @retrieve_age_band_401.1_no_authorization_header + Scenario: No Authorization header + Given the header "Authorization" is removed + And the request body is set to a valid request body + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + + @retrieve_age_band_400.1_invalid_phone_number + Scenario: Phone number value does not comply with the schema + Given the header "Authorization" is set to a valid access token which does not identify a single phone number + And the request body property "$.phoneNumber" does not comply with the OAS schema at "/components/schemas/PhoneNumber" + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 400 + And the response property "$.status" is 400 + And the response property "$.code" is "INVALID_ARGUMENT" + And the response property "$.message" contains a user friendly text + + @retrieve_age_band_422.1_missing_identifier + Scenario: Phone number not included and cannot be deduced from the access token + Given the header "Authorization" is set to a valid access token which does not identify a single phone number + And the request body property "$.phoneNumber" is not included + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 422 + And the response property "$.status" is 422 + And the response property "$.code" is "MISSING_IDENTIFIER" + And the response property "$.message" contains a user friendly text + + @retrieve_age_band_422.2_service_not_applicable + Scenario: Service not available for the phone number + Given that the service is not available for all phone numbers commercialized by the operator + And a valid phone number, identified by the token or provided in the request body, for which the service is not applicable + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 422 + And the response property "$.status" is 422 + And the response property "$.code" is "SERVICE_NOT_APPLICABLE" + And the response property "$.message" contains a user friendly text + + # 501 Not Implemented - operation is optional + + @retrieve_age_band_501_not_implemented + Scenario: Operation not implemented by provider + Given the provider does not implement the retrieve-age-band operation + And the request body is set to a valid request body + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 501 + And the response property "$.status" is 501 + And the response property "$.code" is "NOT_IMPLEMENTED" + And the response property "$.message" contains a user friendly text From 655810a95b8a022cb40e34f892647c4ccaeb354b Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Wed, 9 Sep 2026 10:55:45 +0200 Subject: [PATCH 02/11] Fix: make it clearer that the age range is optional on sim-swap.yaml Co-authored-by: Herbert Damker --- code/API_definitions/sim-swap.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/code/API_definitions/sim-swap.yaml b/code/API_definitions/sim-swap.yaml index c572f81..2d2cf18 100644 --- a/code/API_definitions/sim-swap.yaml +++ b/code/API_definitions/sim-swap.yaml @@ -270,9 +270,10 @@ paths: occurred, expressed as a time band. This operation is an alternative way to expose SIM swap recency for API providers that - do not expose the exact SIM swap date. It does not return the actual SIM swap date and - is not expected to be supported in addition to `check` and `retrieve-date`. Consult your - provider to determine if this optional operation is available; support depends on the provider's + do not expose the exact SIM swap date. It does not return the actual SIM swap date. + This operation is optional: a provider need not implement it in addition to the + mandatory `check` and `retrieve-date` operations, and a provider that does not + implement it returns `501 NOT_IMPLEMENTED`. Support depends on the provider's available capabilities and commercial use case. The returned value is a technical network signal indicating recency. Consuming parties apply their own decisioning From e747d742343f93b029499534202bcbc783f4f51e Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Wed, 9 Sep 2026 10:59:41 +0200 Subject: [PATCH 03/11] Fix: change 30 days to 20 days on test file according to band 10 --- code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature b/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature index f59fafc..2b0ba0d 100644 --- a/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature +++ b/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature @@ -38,7 +38,7 @@ Feature: CAMARA SIM Swap API, vwip - Operation retrieveSimSwapAgeBand @retrieve_age_band_3_mid_age_swap_band_10 Scenario: Retrieve age band showing mid-age SIM swap (band 10) Given a valid phone number identified by the token or provided in the request body - And the SIM for this phone number has been swapped in the last 30 days + And the SIM for this phone number has been swapped in the last 20 days When the request "retrieveSimSwapAgeBand" is sent Then the response status code is 200 And the value of response property "$.simSwapAgeBand" == 10 From b16d4370820b441e513206df6db242931a59ff11 Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Wed, 9 Sep 2026 11:07:06 +0200 Subject: [PATCH 04/11] Fix: sim-swap subscriptions one event, not several description --- code/API_definitions/sim-swap-subscriptions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/API_definitions/sim-swap-subscriptions.yaml b/code/API_definitions/sim-swap-subscriptions.yaml index 59ebda0..01b2729 100644 --- a/code/API_definitions/sim-swap-subscriptions.yaml +++ b/code/API_definitions/sim-swap-subscriptions.yaml @@ -24,7 +24,7 @@ info: These endpoints allow to manage notification subscription on Sim Swap event. The CAMARA subscription model is detailed in the CAMARA API design guideline document and follows Cloudevents specification. Sink credentials support ACCESSTOKEN and PRIVATE_KEY_JWT authentication types. - Since this API manages several event types, it is mandatory to provide the `types` field when creating a subscription. + Since this API manages events for SIM swap notifications, it is mandatory to provide the `types` field when creating a subscription. Only one event `types` is managed for this API: - `org.camaraproject.sim-swap-subscriptions.v0.swapped` - Event triggered when a SIM swap occurs on the associated phone number From ff38c4506650312160572429a6ffc178f1cf9693 Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Wed, 9 Sep 2026 11:13:19 +0200 Subject: [PATCH 05/11] Fix: ApiEventType has exactly one enum value and SubscriptionRequest.types is minItems: 1, maxItems: 1 --- code/API_definitions/sim-swap-subscriptions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/API_definitions/sim-swap-subscriptions.yaml b/code/API_definitions/sim-swap-subscriptions.yaml index 01b2729..9ab8d02 100644 --- a/code/API_definitions/sim-swap-subscriptions.yaml +++ b/code/API_definitions/sim-swap-subscriptions.yaml @@ -24,7 +24,7 @@ info: These endpoints allow to manage notification subscription on Sim Swap event. The CAMARA subscription model is detailed in the CAMARA API design guideline document and follows Cloudevents specification. Sink credentials support ACCESSTOKEN and PRIVATE_KEY_JWT authentication types. - Since this API manages events for SIM swap notifications, it is mandatory to provide the `types` field when creating a subscription. + The `types` field is mandatory when creating a subscription, per the CAMARA subscription model, regardless of how many event types an API defines. Only one event `types` is managed for this API: - `org.camaraproject.sim-swap-subscriptions.v0.swapped` - Event triggered when a SIM swap occurs on the associated phone number From ac8be8fbba80d89a1867eb7115aab1f9cbb4da2b Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Wed, 9 Sep 2026 11:21:52 +0200 Subject: [PATCH 06/11] fix(sim-swap-subscriptions): clarify pagination, credential types, and subscription creation requirements --- code/API_definitions/sim-swap-subscriptions.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/API_definitions/sim-swap-subscriptions.yaml b/code/API_definitions/sim-swap-subscriptions.yaml index 9ab8d02..c49a6b4 100644 --- a/code/API_definitions/sim-swap-subscriptions.yaml +++ b/code/API_definitions/sim-swap-subscriptions.yaml @@ -16,13 +16,13 @@ info: # API Functionality The API exposes following capabilities: - * **/subscriptions**: This endpoint has two operations, one that allows to retrieve the list of Sim Swap event subscriptions and another one to create new event subscriptions. The list operation supports pagination via `page` and `perPage` query parameters returning a `SubscriptionList` response. + * **/subscriptions**: This endpoint has two operations, one that allows to retrieve the list of Sim Swap event subscriptions and another one to create new event subscriptions. The list operation supports pagination via `page` and `perPage` query parameters returning a `SubscriptionList` with subscription items and pagination headers. Creating new event subscriptions requires a sink, protocol, event types, and configuration; sink credentials are optional. * **/subscriptions/{subscriptionId}**: This endpoint also has two operations, one that allows to retrieve a particular event subscription by id and another one to delete an event subscription also using the id. ## Sim Swap notification subscription These endpoints allow to manage notification subscription on Sim Swap event. - The CAMARA subscription model is detailed in the CAMARA API design guideline document and follows Cloudevents specification. Sink credentials support ACCESSTOKEN and PRIVATE_KEY_JWT authentication types. + The CAMARA subscription model is detailed in the CAMARA API design guideline document and follows Cloudevents specification. Sink credentials support the ACCESSTOKEN and PRIVATE_KEY_JWT credential types for securing webhook delivery. The `types` field is mandatory when creating a subscription, per the CAMARA subscription model, regardless of how many event types an API defines. From dd99148be5e56e40a680a6d0835ba80fc9423c5e Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Wed, 9 Sep 2026 11:26:01 +0200 Subject: [PATCH 07/11] fix typo in formating on sim-swap-subscriptions --- .../sim-swap-subscriptions.yaml | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/code/API_definitions/sim-swap-subscriptions.yaml b/code/API_definitions/sim-swap-subscriptions.yaml index c49a6b4..612af37 100644 --- a/code/API_definitions/sim-swap-subscriptions.yaml +++ b/code/API_definitions/sim-swap-subscriptions.yaml @@ -16,7 +16,7 @@ info: # API Functionality The API exposes following capabilities: - * **/subscriptions**: This endpoint has two operations, one that allows to retrieve the list of Sim Swap event subscriptions and another one to create new event subscriptions. The list operation supports pagination via `page` and `perPage` query parameters returning a `SubscriptionList` with subscription items and pagination headers. Creating new event subscriptions requires a sink, protocol, event types, and configuration; sink credentials are optional. + * **/subscriptions**: This endpoint has two operations, one that allows to retrieve the list of Sim Swap event subscriptions and another one to create new event subscriptions. The list operation supports pagination via `page` and `perPage` query parameters returning a `SubscriptionList` with subscription items and pagination headers. Creating new event subscriptions requires a sink, protocol, event types, and configuration; sink credentials are optional. * **/subscriptions/{subscriptionId}**: This endpoint also has two operations, one that allows to retrieve a particular event subscription by id and another one to delete an event subscription also using the id. ## Sim Swap notification subscription @@ -113,7 +113,7 @@ externalDocs: description: Product documentation at CAMARA url: https://github.com/camaraproject/SimSwap servers: - - url: '{apiRoot}/sim-swap-subscriptions/vwip' + - url: "{apiRoot}/sim-swap-subscriptions/vwip" variables: apiRoot: default: http://localhost:9091 @@ -127,7 +127,7 @@ paths: post: tags: - Sim Swap Subscription - summary: 'Create a sim swap event subscription for a phone number' + summary: "Create a sim swap event subscription for a phone number" description: Create a sim swap event subscription for a phone number operationId: createSimSwapSubscription parameters: @@ -143,9 +143,9 @@ paths: $ref: "#/components/schemas/SubscriptionRequest" examples: SUBSCRIPTION_REQUEST: - $ref: '#/components/examples/SUBSCRIPTION_REQUEST' + $ref: "#/components/examples/SUBSCRIPTION_REQUEST" SUBSCRIPTION_REQUEST_3LEGS: - $ref: '#/components/examples/SUBSCRIPTION_REQUEST_3LEGS' + $ref: "#/components/examples/SUBSCRIPTION_REQUEST_3LEGS" required: true callbacks: notifications: @@ -166,9 +166,9 @@ paths: $ref: "#/components/schemas/NotificationEvent" examples: SWAPPED: - $ref: '#/components/examples/SWAPPED' + $ref: "#/components/examples/SWAPPED" SUBSCRIPTION_ENDS: - $ref: '#/components/examples/SUBSCRIPTION_ENDS' + $ref: "#/components/examples/SUBSCRIPTION_ENDS" responses: "204": description: Successful notification @@ -201,9 +201,9 @@ paths: $ref: "#/components/schemas/Subscription" examples: SUBSCRIPTION_RESPONSE: - $ref: '#/components/examples/SUBSCRIPTION_RESPONSE' + $ref: "#/components/examples/SUBSCRIPTION_RESPONSE" SUBSCRIPTION_RESPONSE_3LEGS: - $ref: '#/components/examples/SUBSCRIPTION_RESPONSE_3LEGS' + $ref: "#/components/examples/SUBSCRIPTION_RESPONSE_3LEGS" "202": description: Request accepted to be processed. It applies for async creation process. headers: @@ -228,7 +228,7 @@ paths: get: tags: - Sim Swap Subscription - summary: 'Retrieve a list of sim swap event subscription' + summary: "Retrieve a list of sim swap event subscription" description: Retrieve a list of sim swap event subscription(s) operationId: retrieveSubscriptionList parameters: @@ -256,9 +256,9 @@ paths: $ref: "#/components/schemas/SubscriptionList" examples: SUBSCRIPTIONS: - $ref: '#/components/examples/SUBSCRIPTIONS' + $ref: "#/components/examples/SUBSCRIPTIONS" SUBSCRIPTIONS_3LEGS: - $ref: '#/components/examples/SUBSCRIPTIONS_3LEGS' + $ref: "#/components/examples/SUBSCRIPTIONS_3LEGS" "400": $ref: "../common/CAMARA_common.yaml#/components/responses/Generic400" "401": @@ -269,7 +269,7 @@ paths: get: tags: - Sim Swap Subscription - summary: 'Retrieve a sim swap event subscription for a phone number' + summary: "Retrieve a sim swap event subscription for a phone number" description: retrieve event subscription information for a given subscription. operationId: retrieveSubscription security: @@ -290,9 +290,9 @@ paths: $ref: "#/components/schemas/Subscription" examples: SUBSCRIPTION: - $ref: '#/components/examples/SUBSCRIPTION_RESPONSE' + $ref: "#/components/examples/SUBSCRIPTION_RESPONSE" SUBSCRIPTION_3LEGS: - $ref: '#/components/examples/SUBSCRIPTION_RESPONSE_3LEGS' + $ref: "#/components/examples/SUBSCRIPTION_RESPONSE_3LEGS" "400": $ref: "../common/CAMARA_event_common.yaml#/components/responses/SubscriptionIdRequired400" "401": @@ -304,7 +304,7 @@ paths: delete: tags: - Sim Swap Subscription - summary: 'Delete a sim swap event subscription' + summary: "Delete a sim swap event subscription" operationId: deleteSubscription description: delete a given event subscription. security: @@ -468,7 +468,7 @@ components: discriminator: propertyName: protocol mapping: - HTTP: '#/components/schemas/HTTPSubscriptionResponse' + HTTP: "#/components/schemas/HTTPSubscriptionResponse" # Future protocol support (not yet used in CAMARA): # MQTT3: "#/components/schemas/MQTTSubscriptionResponse" # MQTT5: "#/components/schemas/MQTTSubscriptionResponse" @@ -703,7 +703,7 @@ components: subscriptionDetail: phoneNumber: "+123456789" subscriptionMaxEvents: 10 - subscriptionExpireTime: '2025-01-17T13:18:23.682Z' + subscriptionExpireTime: "2025-01-17T13:18:23.682Z" SUBSCRIPTION_REQUEST_3LEGS: description: The cloud event for a subscription request created in 3-legs value: @@ -718,7 +718,7 @@ components: - org.camaraproject.sim-swap-subscriptions.v0.swapped config: subscriptionDetail: {} - subscriptionExpireTime: '2025-01-17T13:18:23.682Z' + subscriptionExpireTime: "2025-01-17T13:18:23.682Z" SUBSCRIPTION_RESPONSE: description: Subscription details for an active subscription value: @@ -729,10 +729,10 @@ components: config: subscriptionDetail: phoneNumber: "+123456789" - subscriptionExpireTime: '2025-01-17T13:18:23.682Z' - id: '1119920371' - startsAt: '2024-06-07T16:10:15.302Z' - expiresAt: '2024-06-07T16:10:15.302Z' + subscriptionExpireTime: "2025-01-17T13:18:23.682Z" + id: "1119920371" + startsAt: "2024-06-07T16:10:15.302Z" + expiresAt: "2024-06-07T16:10:15.302Z" status: ACTIVATION_REQUESTED SUBSCRIPTION_RESPONSE_3LEGS: description: Subscription details for an active subscription @@ -743,10 +743,10 @@ components: - org.camaraproject.sim-swap-subscriptions.v0.swapped config: subscriptionDetail: {} - subscriptionExpireTime: '2025-01-17T13:18:23.682Z' - id: '1119920371' - startsAt: '2024-06-07T16:10:15.302Z' - expiresAt: '2024-06-07T16:10:15.302Z' + subscriptionExpireTime: "2025-01-17T13:18:23.682Z" + id: "1119920371" + startsAt: "2024-06-07T16:10:15.302Z" + expiresAt: "2024-06-07T16:10:15.302Z" status: ACTIVATION_REQUESTED SUBSCRIPTIONS: description: A list of API consumer subscriptions. @@ -759,10 +759,10 @@ components: config: subscriptionDetail: phoneNumber: "+123456789" - subscriptionExpireTime: '2025-01-17T13:18:23.682Z' - id: '1119920371' - startsAt: '2024-06-07T16:10:15.302Z' - expiresAt: '2024-06-07T16:10:15.302Z' + subscriptionExpireTime: "2025-01-17T13:18:23.682Z" + id: "1119920371" + startsAt: "2024-06-07T16:10:15.302Z" + expiresAt: "2024-06-07T16:10:15.302Z" status: ACTIVATION_REQUESTED pagination: page: 1 @@ -779,10 +779,10 @@ components: - org.camaraproject.sim-swap-subscriptions.v0.swapped config: subscriptionDetail: {} - subscriptionExpireTime: '2025-01-17T13:18:23.682Z' - id: '1119920371' - startsAt: '2024-06-07T16:10:15.302Z' - expiresAt: '2024-06-07T16:10:15.302Z' + subscriptionExpireTime: "2025-01-17T13:18:23.682Z" + id: "1119920371" + startsAt: "2024-06-07T16:10:15.302Z" + expiresAt: "2024-06-07T16:10:15.302Z" status: ACTIVATION_REQUESTED pagination: page: 1 From ef5a48f5173f88a6189e97117a4c0c9f05dc1f9a Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Tue, 15 Sep 2026 19:40:25 +0200 Subject: [PATCH 08/11] Apply batched suggestions from Herbert's suggestions Co-authored-by: Herbert Damker --- code/API_definitions/sim-swap.yaml | 8 ++++ .../sim-swap-retrieveSimSwapAgeBand.feature | 42 ++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/code/API_definitions/sim-swap.yaml b/code/API_definitions/sim-swap.yaml index 2d2cf18..263ba46 100644 --- a/code/API_definitions/sim-swap.yaml +++ b/code/API_definitions/sim-swap.yaml @@ -285,6 +285,14 @@ paths: including a valid subscriber for whom no real-time profile is available, or a provider that cannot support the required granularity or determine the band due to retention limitations — it returns `422 SERVICE_NOT_APPLICABLE` (or does not expose the operation). Transient + The returned value is a technical network signal indicating recency. Consuming parties apply their own decisioning + outside the API contract. Value `111` indicates the provider positively confirms a SIM + swap has never happened and the number has never been ported (a sentinel, not an ordinal + band). A provider that exposes it MUST support the complete standardized + band model. If the service is structurally not applicable for the provided identifier — + including a valid subscriber for whom no real-time profile is available, or a provider that + cannot support the required granularity or determine the band due to retention limitations + — it returns `422 SERVICE_NOT_APPLICABLE` (or does not expose the operation). Transient backend or data-source failures are returned as `5xx`, never `422`. operationId: retrieveSimSwapAgeBand parameters: diff --git a/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature b/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature index 2b0ba0d..43e17f5 100644 --- a/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature +++ b/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature @@ -38,7 +38,7 @@ Feature: CAMARA SIM Swap API, vwip - Operation retrieveSimSwapAgeBand @retrieve_age_band_3_mid_age_swap_band_10 Scenario: Retrieve age band showing mid-age SIM swap (band 10) Given a valid phone number identified by the token or provided in the request body - And the SIM for this phone number has been swapped in the last 20 days + And the SIM for this phone number has been swapped between 14 and 30 days ago When the request "retrieveSimSwapAgeBand" is sent Then the response status code is 200 And the value of response property "$.simSwapAgeBand" == 10 @@ -89,6 +89,26 @@ Feature: CAMARA SIM Swap API, vwip - Operation retrieveSimSwapAgeBand And the response property "$.code" is "UNAUTHENTICATED" And the response property "$.message" contains a user friendly text + @retrieve_age_band_401.2_expired_access_token + Scenario: Expired access token + Given the header "Authorization" is set to an expired access token + And the request body is set to a valid request body + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + + @retrieve_age_band_401.3_invalid_access_token + Scenario: Invalid access token + Given the header "Authorization" is set to an invalid access token + And the request body is set to a valid request body + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + @retrieve_age_band_400.1_invalid_phone_number Scenario: Phone number value does not comply with the schema Given the header "Authorization" is set to a valid access token which does not identify a single phone number @@ -99,6 +119,16 @@ Feature: CAMARA SIM Swap API, vwip - Operation retrieveSimSwapAgeBand And the response property "$.code" is "INVALID_ARGUMENT" And the response property "$.message" contains a user friendly text + @retrieve_age_band_404.1_phone_number_not_found + Scenario: Phone number not found + Given the header "Authorization" is set to a valid access token which does not identify a single phone number + And the request body property "$.phoneNumber" is compliant with the schema but does not identify a valid phone number + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 404 + And the response property "$.status" is 404 + And the response property "$.code" is "IDENTIFIER_NOT_FOUND" + And the response property "$.message" contains a user friendly text + @retrieve_age_band_422.1_missing_identifier Scenario: Phone number not included and cannot be deduced from the access token Given the header "Authorization" is set to a valid access token which does not identify a single phone number @@ -119,6 +149,16 @@ Feature: CAMARA SIM Swap API, vwip - Operation retrieveSimSwapAgeBand And the response property "$.code" is "SERVICE_NOT_APPLICABLE" And the response property "$.message" contains a user friendly text + @retrieve_age_band_422.3_unnecessary_phone_number + Scenario: Phone number not to be included when it can be deduced from the access token + Given the header "Authorization" is set to a valid access token identifying a phone number + And the request body property "$.phoneNumber" is set to a valid phone number + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 422 + And the response property "$.status" is 422 + And the response property "$.code" is "UNNECESSARY_IDENTIFIER" + And the response property "$.message" contains a user friendly text + # 501 Not Implemented - operation is optional @retrieve_age_band_501_not_implemented From d672361b6f8da17500cf3adcfc180a9616fb27cb Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Tue, 15 Sep 2026 19:49:07 +0200 Subject: [PATCH 09/11] fix(test): add missing 401.2, 401.3, C02.02, C02.03 scenarios --- .../sim-swap-retrieveSimSwapAgeBand.feature | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature b/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature index 43e17f5..30ccb98 100644 --- a/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature +++ b/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature @@ -109,6 +109,46 @@ Feature: CAMARA SIM Swap API, vwip - Operation retrieveSimSwapAgeBand And the response property "$.code" is "UNAUTHENTICATED" And the response property "$.message" contains a user friendly text + @retrieve_age_band_C02.02_phone_number_not_found + Scenario: Phone number not found + Given the header "Authorization" is set to a valid access token which does not identify a single phone number + And the request body property "$.phoneNumber" is compliant with the schema but does not identify a valid phone number + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 404 + And the response property "$.status" is 404 + And the response property "$.code" is "IDENTIFIER_NOT_FOUND" + And the response property "$.message" contains a user friendly text + + @retrieve_age_band_C02.03_unnecessary_phone_number + Scenario: Phone number not to be included when it can be deduced from the access token + Given the header "Authorization" is set to a valid access token identifying a phone number + And the request body property "$.phoneNumber" is set to a valid phone number + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 422 + And the response property "$.status" is 422 + And the response property "$.code" is "UNNECESSARY_IDENTIFIER" + And the response property "$.message" contains a user friendly text + + @retrieve_age_band_401.2_expired_access_token + Scenario: Expired access token + Given the header "Authorization" is set to an expired access token + And the request body is set to a valid request body + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + + @retrieve_age_band_401.3_invalid_access_token + Scenario: Invalid access token + Given the header "Authorization" is set to an invalid access token + And the request body is set to a valid request body + When the request "retrieveSimSwapAgeBand" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + @retrieve_age_band_400.1_invalid_phone_number Scenario: Phone number value does not comply with the schema Given the header "Authorization" is set to a valid access token which does not identify a single phone number From 27a6ad25088f3495fdde40116e1e9c1fdb22d19a Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Tue, 15 Sep 2026 22:48:32 +0200 Subject: [PATCH 10/11] fix(test): remove duplicate scenarios in retrieve-age-band tests --- .../sim-swap-retrieveSimSwapAgeBand.feature | 40 ------------------- 1 file changed, 40 deletions(-) diff --git a/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature b/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature index 30ccb98..4b048f1 100644 --- a/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature +++ b/code/Test_definitions/sim-swap-retrieveSimSwapAgeBand.feature @@ -129,26 +129,6 @@ Feature: CAMARA SIM Swap API, vwip - Operation retrieveSimSwapAgeBand And the response property "$.code" is "UNNECESSARY_IDENTIFIER" And the response property "$.message" contains a user friendly text - @retrieve_age_band_401.2_expired_access_token - Scenario: Expired access token - Given the header "Authorization" is set to an expired access token - And the request body is set to a valid request body - When the request "retrieveSimSwapAgeBand" is sent - Then the response status code is 401 - And the response property "$.status" is 401 - And the response property "$.code" is "UNAUTHENTICATED" - And the response property "$.message" contains a user friendly text - - @retrieve_age_band_401.3_invalid_access_token - Scenario: Invalid access token - Given the header "Authorization" is set to an invalid access token - And the request body is set to a valid request body - When the request "retrieveSimSwapAgeBand" is sent - Then the response status code is 401 - And the response property "$.status" is 401 - And the response property "$.code" is "UNAUTHENTICATED" - And the response property "$.message" contains a user friendly text - @retrieve_age_band_400.1_invalid_phone_number Scenario: Phone number value does not comply with the schema Given the header "Authorization" is set to a valid access token which does not identify a single phone number @@ -159,16 +139,6 @@ Feature: CAMARA SIM Swap API, vwip - Operation retrieveSimSwapAgeBand And the response property "$.code" is "INVALID_ARGUMENT" And the response property "$.message" contains a user friendly text - @retrieve_age_band_404.1_phone_number_not_found - Scenario: Phone number not found - Given the header "Authorization" is set to a valid access token which does not identify a single phone number - And the request body property "$.phoneNumber" is compliant with the schema but does not identify a valid phone number - When the request "retrieveSimSwapAgeBand" is sent - Then the response status code is 404 - And the response property "$.status" is 404 - And the response property "$.code" is "IDENTIFIER_NOT_FOUND" - And the response property "$.message" contains a user friendly text - @retrieve_age_band_422.1_missing_identifier Scenario: Phone number not included and cannot be deduced from the access token Given the header "Authorization" is set to a valid access token which does not identify a single phone number @@ -189,16 +159,6 @@ Feature: CAMARA SIM Swap API, vwip - Operation retrieveSimSwapAgeBand And the response property "$.code" is "SERVICE_NOT_APPLICABLE" And the response property "$.message" contains a user friendly text - @retrieve_age_band_422.3_unnecessary_phone_number - Scenario: Phone number not to be included when it can be deduced from the access token - Given the header "Authorization" is set to a valid access token identifying a phone number - And the request body property "$.phoneNumber" is set to a valid phone number - When the request "retrieveSimSwapAgeBand" is sent - Then the response status code is 422 - And the response property "$.status" is 422 - And the response property "$.code" is "UNNECESSARY_IDENTIFIER" - And the response property "$.message" contains a user friendly text - # 501 Not Implemented - operation is optional @retrieve_age_band_501_not_implemented From 7939cea7547a3518eff7653beb50424363f15b61 Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Tue, 15 Sep 2026 22:56:37 +0200 Subject: [PATCH 11/11] fix(sim-swap): remove duplicate retrieveSimSwapAgeBand description --- code/API_definitions/sim-swap.yaml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/code/API_definitions/sim-swap.yaml b/code/API_definitions/sim-swap.yaml index 263ba46..69c762f 100644 --- a/code/API_definitions/sim-swap.yaml +++ b/code/API_definitions/sim-swap.yaml @@ -276,15 +276,6 @@ paths: implement it returns `501 NOT_IMPLEMENTED`. Support depends on the provider's available capabilities and commercial use case. - The returned value is a technical network signal indicating recency. Consuming parties apply their own decisioning - outside the API contract. Value `111` indicates the provider positively confirms a SIM - swap has never happened and the number has never been ported (a sentinel, not an ordinal - band). This operation is OPTIONAL: a provider that does not implement it returns - `501 NOT_IMPLEMENTED`. A provider that exposes it MUST support the complete standardized - band model. If the service is structurally not applicable for the provided identifier — - including a valid subscriber for whom no real-time profile is available, or a provider that - cannot support the required granularity or determine the band due to retention limitations - — it returns `422 SERVICE_NOT_APPLICABLE` (or does not expose the operation). Transient The returned value is a technical network signal indicating recency. Consuming parties apply their own decisioning outside the API contract. Value `111` indicates the provider positively confirms a SIM swap has never happened and the number has never been ported (a sentinel, not an ordinal