Skip to content

Example: ASP.NET Core API client endpoint map

endpoint-map.md is the traceability page between a Core API client and the API it wraps: one row per remote member, naming the verb, the setting that supplies the route, the resolved route, and where that endpoint is documented. It also carries the ## Route drift reconciliation, which is the point of the page for a Core client because every route is read from configuration and no compiler checks it. Doc gen produces one per client at docs/clients/<client-slug>/endpoint-map.md.

What reviewers should check

  • Every Endpoint cell takes one of the template's two forms: a link into ../../apis/<api-slug>/endpoints/<controller-slug>.md when this run generated that API's documentation, or a backticked canonical API name marked † when the API lives in another repository. A drifted row has no endpoint to link and says so in plain words instead of inventing one.
  • Links resolve on disk, and the anchor is the Azure DevOps slug of the endpoint page's <VERB> /<fully-resolved-route> heading. A link into an apis/<slug>/ folder the run never produced is fabrication.
  • The Resolved route column shows the shape the client composes, setting value plus appended parameters as {placeholders}, not one worked example. The Parameter appending column is the expression transcribed from the method body, including the absence of URL encoding.
  • ## Route drift is present, reconciles every remote member against the API's fully resolved route table, and uses the form for a documented API. A member whose verb and route match no endpoint is Drift, and every Drift row is also a HIGH item in Tech debt, linked from the Finding cell.
  • Endpoints the API exposes that the client never calls are listed, so a reader can tell "not wrapped" from "drifted".
  • ## Coverage reconciles: mapped rows plus unmapped rows equals the public member count, and the count of members reconciled in Route drift equals the mapped rows.
  • ## External API origin is absent, because the API is documented by this run.

Example

The document below is a complete, correct client-endpoint-map.md for a fictional ASP.NET Core API client package, including a real route drift row.

View rendered example — the same document as a real page, so you can compare rendering as well as source.

---
title: Endpoint map
summary: Traceability from each client method to the API endpoint it calls, and client-vs-API route drift.
status: draft
owner: Premium Systems Team
last_reviewed: 2026-10-21
---

# Endpoint map

This page traces every remote member of `IPremiumClient` to the `SAIF.Premium.Api` endpoint it calls. It is the page a reader uses to answer "which endpoint does this call hit, which setting supplies its route, and where is that endpoint documented".

| Field | Value |
| --- | --- |
| **Wrapped API** | `SAIF.Premium.Api` |
| **Origin** | Documented by this run |
| **Base URL** | `IEnvironmentClient` setting `premiumbaseurl` (app key `PremiumApi`) |
| **Version segment** | Carried by the route settings (`/v1/...`) |
| **Remote members mapped** | 6 of 7 public members |
| **Route drift findings** | 1 |

## Endpoint map

| Client member | Verb | Setting key | Resolved route | Parameter appending | `IHttpJsonClient` method | Request type | Response type | Endpoint | Reference |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `GetPremiumAsync` | GET | `premiumroute` | `/v1/Premiums/{policyNumber}` | `PremiumRoute + policyNumber` | `GetAsync<T>` | - | `PremiumSummary` | [`GET /v1/Premiums/{policyNumber}`](../../apis/saif-premium-api/endpoints/premiums.md#get-v1premiumspolicynumber) | [Details](reference.md#getpremiumasyncstring) |
| `GetPremiumHistoryAsync` | GET | `premiumhistoryroute` | `/v1/Premiums/{policyNumber}/history` | `string.Format(PremiumHistoryRoute, policyNumber)` | `GetAsync<T>` | - | `List<PremiumTerm>` | [`GET /v1/Premiums/{policyNumber}/history`](../../apis/saif-premium-api/endpoints/premiums.md#get-v1premiumspolicynumberhistory) | [Details](reference.md#getpremiumhistoryasyncstring) |
| `EstimatePremiumAsync` | GET | `estimateroute` | `/v1/Premiums/estimate?classCode={classCode}&payroll={payroll}&effectiveDate={effectiveDate}` | `$"{EstimateRoute}?classCode={classCode}&payroll={payroll}&effectiveDate={effectiveDate:yyyy-MM-dd}"` (no URL encoding) | `GetAsync<T>` | - | `PremiumEstimate` | [`GET /v1/Premiums/estimate`](../../apis/saif-premium-api/endpoints/premiums.md#get-v1premiumsestimate) | [Details](reference.md#estimatepremiumasyncstring-decimal-datetime) |
| `AddAdjustmentAsync` | POST | `adjustmentroute` | `/v1/Premiums/{policyNumber}/adjustment` | `string.Format(AdjustmentRoute, policyNumber)` | `PostAsync<T>` | `PremiumAdjustmentRequest` | `PremiumAdjustment` | No matching endpoint - see [Route drift](#route-drift) | [Details](reference.md#addadjustmentasyncstring-premiumadjustmentrequest) |
| `GetClassCodeRateAsync` | GET | `classcoderoute` | `/v1/ClassCodes/{classCode}/rate` | `$"{ClassCodeRoute}{classCode}/rate"` | `GetAsync<T>` | - | `ClassCodeRate` | [`GET /v1/ClassCodes/{classCode}/rate`](../../apis/saif-premium-api/endpoints/class-codes.md#get-v1classcodesclasscoderate) | [Details](reference.md#getclasscoderateasyncstring) |
| `GetCurrentRateTableAsync` | GET | `ratetableroute` | `/v1/RateTables/current` | `RateTableRoute` | `GetAsync<T>` | - | `RateTable` | [`GET /v1/RateTables/current`](../../apis/saif-premium-api/endpoints/rate-tables.md#get-v1ratetablescurrent) | [Details](reference.md#getcurrentratetableasync) |

Five of the six rows land on an endpoint page generated by this run.
`GetPremiumHistoryAsync` and `AddAdjustmentAsync` build their path with
`string.Format` and a `{0}` placeholder in the setting value, which is why their
Parameter appending cells differ from the plain concatenation used by
`GetPremiumAsync`. `GetCurrentRateTableAsync` is the only member that reaches the
API's minimal API endpoint.

## Route drift

Every remote member is reconciled against the routes [`SAIF.Premium.Api`](../../apis/saif-premium-api/endpoints.md) publishes. A route that matches no endpoint fails at run time with a routing error; the compiler cannot catch it because the route is read from configuration.

| Client member | Verb | Resolved route | Setting key | Closest API route | Finding |
| --- | --- | --- | --- | --- | --- |
| [`GetPremiumAsync`](reference.md#getpremiumasyncstring) | GET | `/v1/Premiums/{policyNumber}` | `premiumroute` | [`GET /v1/Premiums/{policyNumber}`](../../apis/saif-premium-api/endpoints/premiums.md#get-v1premiumspolicynumber) | Matches |
| [`GetPremiumHistoryAsync`](reference.md#getpremiumhistoryasyncstring) | GET | `/v1/Premiums/{policyNumber}/history` | `premiumhistoryroute` | [`GET /v1/Premiums/{policyNumber}/history`](../../apis/saif-premium-api/endpoints/premiums.md#get-v1premiumspolicynumberhistory) | Matches |
| [`EstimatePremiumAsync`](reference.md#estimatepremiumasyncstring-decimal-datetime) | GET | `/v1/Premiums/estimate` | `estimateroute` | [`GET /v1/Premiums/estimate`](../../apis/saif-premium-api/endpoints/premiums.md#get-v1premiumsestimate) | Matches |
| [`AddAdjustmentAsync`](reference.md#addadjustmentasyncstring-premiumadjustmentrequest) | POST | `/v1/Premiums/{policyNumber}/adjustment` | `adjustmentroute` | [`POST /v1/Premiums/{policyNumber}/adjustments`](../../apis/saif-premium-api/endpoints/premiums.md#post-v1premiumspolicynumberadjustments) | **Drift** - client route ends in `adjustment`, API literal is `adjustments`; fails at run time with a 404; see [Tech debt](tech-debt.md#high) |
| [`GetClassCodeRateAsync`](reference.md#getclasscoderateasyncstring) | GET | `/v1/ClassCodes/{classCode}/rate` | `classcoderoute` | [`GET /v1/ClassCodes/{classCode}/rate`](../../apis/saif-premium-api/endpoints/class-codes.md#get-v1classcodesclasscoderate) | Matches |
| [`GetCurrentRateTableAsync`](reference.md#getcurrentratetableasync) | GET | `/v1/RateTables/current` | `ratetableroute` | [`GET /v1/RateTables/current`](../../apis/saif-premium-api/endpoints/rate-tables.md#get-v1ratetablescurrent) | Matches |

The drift is a one-character difference in the seeded `adjustmentroute` setting:
the API publishes the collection as `adjustments`, the client posts to
`adjustment`. The request reaches the service and returns `404`, which is
indistinguishable from a missing policy.

### Endpoints not called by the client

| API endpoint | Why the client does not call it |
| --- | --- |
| [`GET /v2/ClassCodes/{classCode}/rate`](../../apis/saif-premium-api/endpoints/class-codes.md#get-v2classcodesclasscoderate) | The client targets `v1` only; no member or setting selects `v2` |
| [`POST /v1/Admin/RecalculateBatch`](../../apis/saif-premium-api/endpoints/admin.md#post-v1adminrecalculatebatch) | Administrative endpoint; no client member wraps it |
| [`POST /v1/Premiums/{policyNumber}/adjustments`](../../apis/saif-premium-api/endpoints/premiums.md#post-v1premiumspolicynumberadjustments) | Intended target of `AddAdjustmentAsync`; missed because of the drift above |

## Unmapped members

Members below are part of the public surface but call no endpoint. They are listed here so the map is provably complete against [Client reference](reference.md).

| Client member | Why unmapped |
| --- | --- |
| `NormalizePolicyNumber(string)` | Local computation - trims and upper-cases a policy number; issues no request |

## Coverage

| Check | Result |
| --- | --- |
| Public members in `IPremiumClient` | 7 |
| Rows in the endpoint map | 6 |
| Rows in unmapped members | 1 |
| Every mapped row resolves, is †-marked, or is unresolved with a reason | No - 5 of 6 resolve; `AddAdjustmentAsync` matches no endpoint (see Route drift) |
| Remote members reconciled in Route drift | 6 |

## Related documentation
- [Client index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Package](package.md)
- [Client reference](reference.md)
- [Consumption](consumption.md)
- [Contracts](contracts.md)
- [Dependencies](dependencies.md)
- [Business rules](business-rules.md)
- [Tech debt](tech-debt.md)

> **Note:** Statements marked with † were retrieved from dependency library documentation
> via the Legends CLI RAG service. They describe inherited behavior from the indicated
> origin library and should be verified if the dependency version changes.

## Sources
- `src/SAIF.Premium.Api.Client/IPremiumClient.cs`
- `src/SAIF.Premium.Api.Client/PremiumClient.cs`
- `src/SAIF.Premium.Api.Client/PremiumClientConfiguration.cs`
- `src/SAIF.Premium.Api/Controllers/PremiumsController.cs`
- `src/SAIF.Premium.Api/Controllers/ClassCodesController.cs`
- `src/SAIF.Premium.Api/Controllers/AdminController.cs`
- `src/SAIF.Premium.Api/Endpoints/RateTableEndpoints.cs`
- `environmentsettings/settings.yml`