Skip to content

Example: API client reference

reference.md documents every public member of a client interface, one section per member, so a caller can use the client without opening its source. Doc gen produces one per client at docs/clients/<client-slug>/reference.md.

What reviewers should check

  • Every public member of the interface has its own H2. Overloads are separate members and each gets its own section with a disambiguating parameter list in the heading — "see the overload above" fails the coverage gate.
  • Public members declared on the class but not on the interface — a separate Initialize method is the usual case — appear here too. The interface is not the whole public surface.
  • Each section is self-contained. A section that omits its own signature, URL, DTOs, or exceptions because a neighbouring member is near-identical is a defect, however repetitive the result looks.
  • ### URL composition reproduces the expression as written in source, and the segment table says how each segment or query parameter is bound — encoding, enum-to-string conversion, and defaults included.
  • ### Exceptions is never blank. "None thrown by the client; every failure surfaces from JsonHttpClient" is a finding worth writing down, and exception types retrieved from dependency documentation carry †.
  • ## Coverage checklist reconciles exactly: one row per interface member, in declaration order, each linking to its section. A row with no section, or a section with no row, is a coverage failure.

Example

The document below is a complete, correct client-reference.md for a fictional API client package.

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

---
title: Client reference
summary: Complete reference for every public member of the client interface.
status: draft
owner: Agency Systems Team
last_reviewed: 2026-09-10
---

# Client reference

This page documents every public member of `IAgencyClient` as implemented by `AgencyClient`.

---

## GetAgency(string)

Returns the single agency record identified by its agency number, or `null` when
the API reports that no agency carries that number.

| Field | Value |
| --- | --- |
| **Signature** | `Agency GetAgency(string agencyNumber)` |
| **Kind** | Remote |
| **HTTP verb** | GET |
| **Resolved URL** | `<baseUrl>v1/agencies/{agencyNumber}` |
| **Transport member** | `GetResult<T>` |
| **Request DTO** | — |
| **Response DTO** | `Agency` |
| **Endpoint** | [`GET /v1/agencies/{agencyNumber}`](endpoint-map.md#method-to-endpoint-map) — see the row for `GetAgency` |
| **Source** | `src\SAIF.Agency.Api.Client\AgencyClient.cs:58` |

### URL composition

```csharp
var url = $"agencies/{Uri.EscapeDataString(agencyNumber)}";
```

| Segment | Bound from | Notes |
| --- | --- | --- |
| `agencies` | Literal | The `v1/` prefix is already part of the base URI supplied to the constructor |
| `{agencyNumber}` | `agencyNumber` | URL-encoded with `Uri.EscapeDataString`; no case normalization, so `a-1042` and `A-1042` are sent as written |

### Parameters

| Parameter | Type | Required | Default | Purpose |
| --- | --- | --- | --- | --- |
| `agencyNumber` | `string` | Yes | — | SAIF agency number, for example `A-1042` |

### Returns

`GetResult<Agency>` deserializes the response body directly into an `Agency`, so
the member returns the entity with no envelope unwrapping. A `404` from the API
is translated by `JsonHttpClient` into a `null` result rather than an
exception,† so callers must null-check before dereferencing. All other
non-success statuses throw.

### Exceptions

| Exception | Thrown by | Condition |
| --- | --- | --- |
| `ArgumentException` | `AgencyClient` | `agencyNumber` is null, empty, or whitespace; checked before any HTTP call is made |
| `ApiClientException` | `JsonHttpClient` | Non-success status other than `404`, including `500` from the agency database |
| `TaskCanceledException` | `JsonHttpClient` | The 30-second client timeout elapsed before the response completed† |
| `JsonSerializationException` | `JsonHttpClient` | The response body could not be deserialized into `Agency`† |

### Notes
- The guard clause is the only client-side validation on this member; agency
  number format is not checked, so a malformed number reaches the API and
  returns `null`.
- No caching. Two consecutive calls for the same agency number issue two
  requests.

---

## SearchAgencies(string, bool)

Returns the agencies associated with a producer code, optionally restricted to
agencies that are currently active.

| Field | Value |
| --- | --- |
| **Signature** | `IList<AgencySummary> SearchAgencies(string producerCode, bool activeOnly)` |
| **Kind** | Remote |
| **HTTP verb** | GET |
| **Resolved URL** | `<baseUrl>v1/agencies?producerCode={producerCode}&activeOnly={activeOnly}` |
| **Transport member** | `GetResults<T>` |
| **Request DTO** | — |
| **Response DTO** | `AgencySummary` |
| **Endpoint** | [`GET /v1/agencies`](endpoint-map.md#method-to-endpoint-map) — see the row for `SearchAgencies` |
| **Source** | `src\SAIF.Agency.Api.Client\AgencyClient.cs:79` |

### URL composition

```csharp
var url = $"agencies?producerCode={Uri.EscapeDataString(producerCode)}&activeOnly={activeOnly.ToString().ToLowerInvariant()}";
```

| Segment | Bound from | Notes |
| --- | --- | --- |
| `producerCode` | `producerCode` | URL-encoded query value; always sent, even when empty |
| `activeOnly` | `activeOnly` | `Boolean.ToString()` lowered to `true` / `false` so the API's model binder accepts it; `false` is sent explicitly rather than omitted |

### Parameters

| Parameter | Type | Required | Default | Purpose |
| --- | --- | --- | --- | --- |
| `producerCode` | `string` | Yes | — | Producer code to search on, for example `PRD-338` |
| `activeOnly` | `bool` | Yes | — | `true` restricts results to agencies with an active appointment; there is no overload that defaults it |

### Returns

`GetResults<AgencySummary>` deserializes the response body's array into an
`IList<AgencySummary>`. A search that matches nothing returns an empty list, not
`null` and not an exception, so callers should test `Count` rather than
null-check.

### Exceptions

| Exception | Thrown by | Condition |
| --- | --- | --- |
| `ArgumentException` | `AgencyClient` | `producerCode` is null, empty, or whitespace |
| `ApiClientException` | `JsonHttpClient` | Non-success status, including `400` when the API rejects the producer code format |
| `TaskCanceledException` | `JsonHttpClient` | The 30-second client timeout elapsed before the response completed† |
| `JsonSerializationException` | `JsonHttpClient` | The response body could not be deserialized into `AgencySummary[]`† |

### Notes
- The API caps a search at 200 summaries and does not paginate; the client
  neither detects nor reports truncation.
- `AgencySummary` is a projection, not `Agency`: it carries agency number, name,
  producer code, and status only. Callers that need mailing or contact details
  follow up with `GetAgency`.

---

## GetAgentsForAgency(string)

Returns the agents currently appointed at an agency, in the order the API
returns them (surname, then given name).

| Field | Value |
| --- | --- |
| **Signature** | `IList<Agent> GetAgentsForAgency(string agencyNumber)` |
| **Kind** | Remote |
| **HTTP verb** | GET |
| **Resolved URL** | `<baseUrl>v1/agencies/{agencyNumber}/agents` |
| **Transport member** | `GetResults<T>` |
| **Request DTO** | — |
| **Response DTO** | `Agent` |
| **Endpoint** | [`GET /v1/agencies/{agencyNumber}/agents`](endpoint-map.md#method-to-endpoint-map) — see the row for `GetAgentsForAgency` |
| **Source** | `src\SAIF.Agency.Api.Client\AgencyClient.cs:101` |

### URL composition

```csharp
var url = $"agencies/{Uri.EscapeDataString(agencyNumber)}/agents";
```

| Segment | Bound from | Notes |
| --- | --- | --- |
| `agencies` | Literal | — |
| `{agencyNumber}` | `agencyNumber` | URL-encoded with `Uri.EscapeDataString` |
| `agents` | Literal | Sub-resource segment; there is no query parameter to include terminated agents |

### Parameters

| Parameter | Type | Required | Default | Purpose |
| --- | --- | --- | --- | --- |
| `agencyNumber` | `string` | Yes | — | SAIF agency number whose appointed agents are requested |

### Returns

`GetResults<Agent>` deserializes the response array into an `IList<Agent>`. An
agency with no active agents returns an empty list. An agency number that does
not exist also returns an empty list rather than `null`, because the API answers
`200` with an empty array for an unknown parent; callers that need to
distinguish the two cases call `GetAgency` first.

### Exceptions

| Exception | Thrown by | Condition |
| --- | --- | --- |
| `ArgumentException` | `AgencyClient` | `agencyNumber` is null, empty, or whitespace |
| `ApiClientException` | `JsonHttpClient` | Non-success status returned by the agents sub-resource |
| `TaskCanceledException` | `JsonHttpClient` | The 30-second client timeout elapsed before the response completed† |
| `JsonSerializationException` | `JsonHttpClient` | The response body could not be deserialized into `Agent[]`† |

### Notes
- Only currently appointed agents are returned; appointment history is served by
  `v1/appointments` and is not exposed by this client.
- `SAIF.Policy.RenewalNotice` calls this member once per agency, not once per
  policy, and caches the result for the run.

---

## UpdateAgencyContact(string, AgencyContactUpdate)

Replaces the mailing address, phone, and email on an agency's contact record and
returns the stored values as the API persisted them.

| Field | Value |
| --- | --- |
| **Signature** | `AgencyContact UpdateAgencyContact(string agencyNumber, AgencyContactUpdate update)` |
| **Kind** | Remote |
| **HTTP verb** | PUT |
| **Resolved URL** | `<baseUrl>v1/agencies/{agencyNumber}/contact` |
| **Transport member** | `PutJson<T>` |
| **Request DTO** | `AgencyContactUpdate` |
| **Response DTO** | `AgencyContact` |
| **Endpoint** | [`PUT /v1/agencies/{agencyNumber}/contact`](endpoint-map.md#method-to-endpoint-map) — see the row for `UpdateAgencyContact` |
| **Source** | `src\SAIF.Agency.Api.Client\AgencyClient.cs:124` |

### URL composition

```csharp
var url = $"agencies/{Uri.EscapeDataString(agencyNumber)}/contact";
```

| Segment | Bound from | Notes |
| --- | --- | --- |
| `agencies` | Literal | — |
| `{agencyNumber}` | `agencyNumber` | URL-encoded with `Uri.EscapeDataString` |
| `contact` | Literal | The agency number is carried in the route only; `AgencyContactUpdate` does not repeat it |

### Parameters

| Parameter | Type | Required | Default | Purpose |
| --- | --- | --- | --- | --- |
| `agencyNumber` | `string` | Yes | — | Agency whose contact record is replaced |
| `update` | `AgencyContactUpdate` | Yes | — | Complete replacement contact record: `AddressLine1`, `AddressLine2`, `City`, `State`, `PostalCode`, `Phone`, `Email` |

### Returns

`PutJson<AgencyContact>` returns an envelope, from which this member selects
`Results.FirstOrDefault()` and returns the resulting `AgencyContact`.† The
returned record reflects the API's normalization — state lowered to a two-letter
code, phone digits stripped of formatting — so a caller that needs the stored
form should use the return value rather than the object it sent.

### Exceptions

| Exception | Thrown by | Condition |
| --- | --- | --- |
| `ArgumentException` | `AgencyClient` | `agencyNumber` is null, empty, or whitespace |
| `ArgumentNullException` | `AgencyClient` | `update` is null |
| `ApiClientException` | `JsonHttpClient` | Non-success status, including `400` when the API's contact validation rejects the payload and `409` when the agency is terminated |
| `TaskCanceledException` | `JsonHttpClient` | The 30-second client timeout elapsed before the response completed† |
| `JsonSerializationException` | `JsonHttpClient` | The response envelope could not be deserialized into `AgencyContact`† |

### Notes
- This is a replacement, not a merge. A null or omitted property on
  `AgencyContactUpdate` clears the stored value; the client does not read the
  current contact record first.
- `Results.FirstOrDefault()` returns `null` if the API ever answers with an
  empty envelope. No caller has observed this, but the member does not guard
  against it.
- The call is not idempotent from an audit perspective: the API writes an
  `AGENCY.CONTACT_AUDIT` row per request even when the values are unchanged.*

---

## Coverage checklist

| Interface member | Documented section | Kind |
| --- | --- | --- |
| `Agency GetAgency(string agencyNumber)` | [`GetAgency`](#getagencystring) | Remote |
| `IList<AgencySummary> SearchAgencies(string producerCode, bool activeOnly)` | [`SearchAgencies`](#searchagenciesstring-bool) | Remote |
| `IList<Agent> GetAgentsForAgency(string agencyNumber)` | [`GetAgentsForAgency`](#getagentsforagencystring) | Remote |
| `AgencyContact UpdateAgencyContact(string agencyNumber, AgencyContactUpdate update)` | [`UpdateAgencyContact`](#updateagencycontactstring-agencycontactupdate) | Remote |

## Related documentation
- [Client index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Package](package.md)
- [Endpoint map](endpoint-map.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.Agency.Api.Client/IAgencyClient.cs`
- `src/SAIF.Agency.Api.Client/AgencyClient.cs`
- `src/SAIF.Agency.Api.Core/Agency.cs`
- `src/SAIF.Agency.Api.Core/AgencySummary.cs`
- `src/SAIF.Agency.Api.Core/Agent.cs`
- `src/SAIF.Agency.Api.Core/AgencyContact.cs`
- `src/SAIF.Agency.Api.Core/AgencyContactUpdate.cs`