Skip to content

feat: add /retrieve-age-band (SIM swap age band) endpoint#273

Open
KeldaAnders wants to merge 5 commits into
camaraproject:mainfrom
KeldaAnders:feat/sim-swap-age-band-endpoint
Open

feat: add /retrieve-age-band (SIM swap age band) endpoint#273
KeldaAnders wants to merge 5 commits into
camaraproject:mainfrom
KeldaAnders:feat/sim-swap-age-band-endpoint

Conversation

@KeldaAnders

@KeldaAnders KeldaAnders commented Jun 9, 2026

Copy link
Copy Markdown

Title: feat: add /retrieve-age-band (SIM swap age band) endpoint

Adds a standalone, single-purpose operation that returns a standardized SIM swap recency band (simSwapAgeBand) only. Supersedes #266 (which was opened from main by mistake) and incorporates that PR's review feedback. Targeting release 2.1.0.

What type of PR is this?

enhancement/feature

What this PR does / why we need it:

Adds POST /retrieve-age-band to the SIM Swap API: an alternative way to expose SIM swap recency for providers that do not expose the exact SIM swap date. It returns a time-bucketed value indicating how recently a SIM swap/activation occurred — and nothing else.

  • Purely additive. /check and /retrieve-date (and their schemas, request bodies, and examples) are preserved unchanged. The only additions are the new path, the SimSwapAgeBand / SimSwapAgeBandInfo / CreateSimSwapAgeBand schemas, the AGEBAND_* examples, the new scope, and the related description updates.
  • Single-purpose by design. The response carries the band value only. It does not include the swapped Boolean (/check) or the SIM change timestamp (/retrieve-date).
  • Alternative, not mandatory. Providers are not expected to support this in addition to check/retrieve-date; support depends on the provider's capabilities and commercial use case.
  • Band values: 115 represent increasing recency bands (4h → 3y+); 999 indicates no SIM swap event was found. When the information is unavailable for the subscriber, the request returns 422 SERVICE_NOT_APPLICABLE.
  • New scope sim-swap:retrieve-age-band, alongside sim-swap:check, sim-swap:retrieve-date, and the API-level sim-swap scope.

Adding a new endpoint is backward-compatible, so this targets a MINOR bump to 2.1.0 (URL path unchanged: /sim-swap/v2).

Which issue(s) this PR fixes:

Fixes #260
Fixes #253

Changes after the #266 review

  • Renamed the field from simSwapAgeBandEnum to simSwapAgeBand (the schema already defines it as an enum). — per @jpengar
  • Moved the "no real-time profile" case out of the success enum. -1 is removed; unavailable information is now an error (422 SERVICE_NOT_APPLICABLE) rather than mixing time-bucket and error semantics. — per @jpengar / @bigludo7
  • Generalized the error wording. Provider-specific phrasing ("no real-time profile found") is replaced with a generic message applicable to any provider.
  • Removed scoring/business-logic language. "fraud decisioning / risk scoring / ML features / step-up logic" is replaced with neutral wording: the value is a technical network signal indicating recency; how a consumer uses it is up to them. — per @jpengar
  • Design rationale kept in PR comments, not embedded in the spec (removed the in-spec Commonalities NOTE). — per @jpengar

Special notes for reviewers:

  • Open — "no swap" representation. This PR uses 999 for "no SIM swap found." @jpengar proposed instead making the field nullable: true and returning null, keeping the enum 115; @bigludo7 raised whether integer + null is valid (it is, via nullable: true in OAS 3.0.x). Please confirm which you prefer and I'll align.
  • Open — buckets vs. timestamp. The time-bucket vs. absolute-timestamp discussion is still pending (CC @HuubAppelboom). This PR assumes time buckets.
  • 422 code. Unavailable information uses the existing SERVICE_NOT_APPLICABLE. Please confirm this is the right Commonalities code or whether a dedicated one is preferred.
  • Additive. The diff shows no deletions in /check or /retrieve-date. Supersedes Update sim-swap | add /retrieve-age-band endpoint returning age band … #266 and addresses the scope concern from Update sim-swap.yaml with age band #254.

Changelog input

Added a new `POST /retrieve-age-band` endpoint returning a standardized SIM swap recency band (`simSwapAgeBand`, values 1–15, or 999 when no swap is found) as an alternative to the exact SIM swap date. Unavailable information returns 422 SERVICE_NOT_APPLICABLE. Existing `/check` and `/retrieve-date` endpoints are unchanged.

Additional documentation

This section can be blank.


This PR is opened from the feature branch KeldaAnders:feat/sim-swap-age-band-endpoint and supersedes the previously opened PR #266 which was created from main.

…ge band only

Returns the standardized simSwapAgeBandEnum only; excludes the swapped
Boolean (/check) and the change date (/retrieve-date) per the 2026-04-29
ad-hoc agreement (camaraproject#253) and TEF stance (camaraproject#260).
@HuubAppelboom

Copy link
Copy Markdown

@KeldaAnders For our largest customer (40 million accounts) requires a more granular age band (they use 72 hours). Can you add more age bands in the first week to accomodate this ?

@HuubAppelboom

Copy link
Copy Markdown

@KeldaAnders And what to return in the first 24 hours if your regulator only allows days as minimum recency ? And what is there is an upper limit that is allowed to be returned ??

@KeldaAnders

KeldaAnders commented Jun 10, 2026

Copy link
Copy Markdown
Author

@HuubAppelboom Thanks for sharing both points.

On finer granularity in the first week (your 72h case). Granularity at the recent end (where time-sensitive decisions cluster) is reasonable. A couple of things to align on first:

The enum is a standardized, cross-operator set (the agreement behind #253/#260), so the goal is to choose standard boundaries that let common decision thresholds land on a band edge — rather than adding customer-specific bands, which would fragment the standard and break interoperability.

Your 72h example is a good illustration of a potential gap: 72h = 3d falls inside the current 2d ≤ d < 5d band, so a consumer can't currently threshold precisely at 72h. The minimal fix is to split that band at 3d:

  • 2d ≤ d < 3d
  • 3d ≤ d < 5d

That makes a 72h cut expressible without any per-customer logic.

Could you share the full set of thresholds your customers actually decide on (72h noted) and the associated business value? If we collect those (e.g. 24h / 48h / 72h / 7d), I'll propose a revised standard band set to the WG that keeps fine granularity in the first days and stays coarse later.

On day-level granularity and the upper limit. First, adding a monitoredPeriod to this endpoint adds unnecessary complexity and shifts the business context of /retrieve-age-band.

The monitored-period / retention context is already fully served by /retrieve-date, which returns the exact date together with the supervision window.

So a consumer that needs the provider's monitored window — or whether the event predates it — uses /retrieve-date. Duplicating monitoredPeriod on the age-band response would change its results and semantics for no real gain, and it cuts against keeping each endpoint single-purpose.

For /retrieve-age-band specifically, that means:

  • The bands are a maximal standard set; a provider is not required to populate & /or is not required to support this endpoint at all. /retrieve-age-band is an alternative, not a mandatory endpoint. If exposing the age band (or this level of granularity) isn't something a provider can or wants to support, it simply offers /check and/or /retrieve-date instead — those remain the baseline way to get SIM swap information, and a consumer falls back to them when a given provider doesn't offer the age band.

TL;DR: /retrieve-age-band stays band-only and single-purpose — an optional alternative, with /check and /retrieve-date as the fallback and the home for date/monitored-period context. Band boundaries (e.g., a 3d split for 72h) can be refined once you've shared the threshold list.

@jpengar @bigludo7 @shilpa-padgaonkar — does that separation work for you?

cc @yyeAduna, @ReidErickson-TMO @jgarciahospital,

@HuubAppelboom

Copy link
Copy Markdown

@KeldaAnders For us it is sufficient to add the 72 hours band, that seems to be the standard here, but maybe others have different use cases as well. In any case, would it hurt to add 96 hours as well ??

Regarding telco's that are only allowed a precision of at least 24 hours, I have a different proposal. In stead of returning a single band as the answer, simply allow for example "<=3" (in stead of 1, 2 or 3). That indicates that it should fall in category 1,2 or 3.

Likewise for the upper range, if you don't want to go further than for example than 1 year back, you simply provide ">=13" in stead of 13, 14 or 15.

That way it is self contained, no extra parameters are required, and these cases can also be covered.
Also, in case you have an international customer that wants a global age band api, it becomes easier to map check api's and retrieve-date api's to a single age band api.

@KeldaAnders

Copy link
Copy Markdown
Author

@HuubAppelboom

72h / 96h bands — accepted. I'm adding both boundaries. The old 2d ≤ d < 5d band is now split into:

  • 5 = 2d ≤ d < 3d (≤ 72h)
  • 6 = 3d ≤ d < 4d (72h–96h)
  • 7 = 4d ≤ d < 5d

That gives clean 72h and 96h cut points, and the set runs 117 plus 999 (no swap found). Adding 96h now costs nothing and saves a second enum change later, so agreed. If others surface further early-week thresholds, we can fold those in during the same WG pass.

On the <=3 / >=13 range notation. I'd rather not take this on, and I'd prefer we move forward with the current version and get it approved as-is.

The notation changes the response field from a plain integer to a small grammar — exact N, <=N, >=N — which means every consumer has to parse the value and we lose simple enum validation (we'd be validating against a pattern instead). Layering a comparator syntax on top now would widen the scope and risk stalling the PR over something we don't yet have a concrete requirement for.

A fixed integer band set (117, plus 999) is simpler to implement, validate, and consume, and it already covers the agreed use cases. If a real need for limited-precision or limited-retention encoding emerges down the line, we can revisit it as a focused follow-up rather than holding this PR for it.

@jpengar @bigludo7 @shilpa-padgaonkar — assuming the 72h/96h additions look good, can we proceed to approval?

…enumber to 1-17

Signed-off-by: Kelda <kelda.anders@gmail.com>
Comment thread code/API_definitions/sim-swap.yaml
…missing data), and consistent 'SIM swap event' wording

Signed-off-by: Kelda Anders <kelda.anders@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants