# Implement Endpoint

> Implement or retrofit one FreeAgent endpoint end-to-end with strict model guardrails, sample app sync, and plan-first workflow. Use when adding or updating SDK resource services, models, tests, or sample pages for a FreeAgent API entity.

- Skill: `markheydon/implement-endpoint` (Agent Skill)
- Install (CLI): `npx skillmds@latest add markheydon/implement-endpoint`
- Raw SKILL.md: https://api.skillmd.com/api/skills/markheydon/implement-endpoint/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: markheydon (https://skillmd.com/u/markheydon)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/markheydon/implement-endpoint

---


# Implement Endpoint

Implement one FreeAgent endpoint page end-to-end for this repository, including SDK models and wrappers, service methods, tests, sample app page and navigation sync, and README/API coverage updates.

If the entity already exists, retrofit it to current guardrails in the same run.

## Required Inputs

- `EntityName` (required)
- `DocsUrlOverride` (optional FreeAgent docs URL)

## Step 1 — Validate Entity and Inventory Operations

1. Resolve the docs index: https://dev.freeagent.com/docs/index
2. Validate that `EntityName` maps to a real docs page.
3. If no matching endpoint page exists, stop immediately and report the invalid entity.
4. If `DocsUrlOverride` is provided, validate and use that page.
5. **Fetch the docs page and list every operation heading** (`##` / `###`) before planning — for example "List all categories", "Get a single category", "Create an income category", "Create a cost of sales category", "Update an admin expenses category". The unit of implementation is the **documented operation heading**, not the HTTP route alone.

## Step 2 — Plan First (New or Retrofit)

1. Produce a concise implementation plan before editing code.
2. If the entity already exists, audit existing models and services against these guardrails:
   - Every serialised property has `JsonPropertyName`
   - Date-only API fields use `DateOnly` (not `DateTime`)
   - Timestamp fields use `DateTimeOffset` (not `DateTime`)
   - Constrained string fields are enums or strong value mappings with exact wire values
   - Response payloads use explicit wrapper/envelope models
   - Services validate wrappers and throw `FreeAgentApiException` on missing payload
3. Flag any guardrail violations as retrofit tasks in the plan.
4. Include in the plan:
   - **Operation heading inventory** from Step 1
   - A table mapping **heading → HTTP route → SDK method → request type → allowed fields** (new) or existing service gaps (retrofit)
   - **Documented use-case variants** — when the API docs describe multiple create/update shapes for the same route (for example income vs cost-of-sales categories), list each variant and the typed SDK method/request type it will map to
   - New files vs retrofit files (with specific violations listed)
   - Breaking API-surface changes expected
   - Test, sample app, and documentation changes

Proceed to implementation only after the plan is complete.

## Step 3 — Models and Guardrails

Apply to all new and retrofitted models:

1. Use `System.Text.Json`.
2. Every serialised/deserialised property must have `JsonPropertyName`.
3. Use `DateOnly` for date-only API fields.
4. Use `DateTimeOffset` for timestamp fields (not `DateTime`).
5. Constrained string fields must use enums or strong value mappings with exact wire-value behaviour.
6. API-facing enums must use explicit `JsonStringEnumMemberName` wire values for each enum member.
7. When docs are ambiguous for constrained values, do not silently guess; mark unresolved mapping and add a follow-up issue.
8. Response payloads must use explicit wrapper/envelope models.
9. Missing required payload branches must throw `FreeAgentApiException`.
10. **Per-variant allowed values:** when allowed wire keys differ by operation variant, use a distinct enum on that request only. When they also differ by a documented discriminator (for example company type), use discriminator-specific enums and factory methods — do not flatten into one enum, do not leave `string` plus "see the API docs", and do not add a runtime validator that fetches Company or account settings. See [adr-0010-documented-operations-to-sdk-methods.md](../../adr/adr-0010-documented-operations-to-sdk-methods.md).

Common retrofit violations:

- `DateTime` instead of `DateOnly` for date-only fields
- `DateTime` instead of `DateTimeOffset` for timestamps
- String fields that should be enums per API docs
- Missing `JsonPropertyName`
- Response wrappers not validated in service methods
- Generic create/update payload that unions all variant attributes

## Step 4 — Services and Pagination

1. Follow existing service structure under `src/FreeAgent.Client/Services/`.
2. Keep methods async and accept `CancellationToken`.
3. When the API paginates list results, provide both single-page and auto-pagination methods. Do not invent pagination for endpoints that return a complete collection (for example Categories).
4. Where the API accepts `per_page`, respect the FreeAgent maximum of 100 and fail fast if the caller exceeds it.
5. **Documented use-case variants:** when the API docs describe multiple create or update shapes for the same HTTP route, expose a separate public request type and service method per variant (for example `CreateIncomeCategoryAsync`, `CreateCostOfSalesCategoryAsync`). Each request type must include only the attributes allowed for that variant. Fixed wire values such as `category_group` are set by the SDK — callers must not supply them. Do not expose a single generic create/update that forces consumers to read external docs to learn which fields apply.
6. **Documented local contract checks:** fail fast only on constraints the official docs state that do not require account state. Do not invent extra validation, uniqueness checks, or fetches of Company/settings. Categories nominal-code ranges are one documented example, not a pattern to copy onto undocumented fields.

## Step 5 — Tests

Add or update tests to cover:

- URL construction
- Envelope/wrapper deserialisation
- Date handling (`DateOnly`)
- Enum/string wire mapping exactness
- Missing payload branch exceptions
- Pagination behaviour and cancellation **when the API paginates**
- **At least one test per documented write variant** — assert URL, envelope, and which fields are included or excluded in the serialised payload

## Step 6 — Sample App Sync

Follow the probe-page standard documented in [`docs/contributing/sample-probe-pages.md`](../../docs/contributing/sample-probe-pages.md). Use **Company** (single GET), **Contacts** (paginated list + CRUD), and **Categories** (non-paginated list + multi-variant writes) as reference implementations.

1. Add or update page(s) under `samples/FreeAgent.Client.BlazorSample/Components/Pages/`.
2. Update navigation in `samples/FreeAgent.Client.BlazorSample/Components/Layout/MainLayout.razor`.
3. Do not add sample UI for endpoints not implemented in SDK.
4. On each probe page, include:
   - `EndpointProbeHeader` (call under test, `DocsUrl`, environment, endpoint path)
   - `ModelProbeResults` built via `ModelWireDiagnostics.Build(...)` after successful SDK calls
   - `ApiErrorDiagnostics` on failures
   - A readable raw JSON section (provided by `ModelProbeResults`)
5. For **list** endpoints: per-row mapping inspection from the wire array item (see `Contacts.razor`).
6. For **CRUD** endpoints: detail page with `?id=` deep links; fetch wire JSON after create/update; show `MudProgressLinear` while operations run (see `ContactDetail.razor`).
7. When the SDK exposes **multiple write variants** for the same resource, the sample must be able to invoke each public write method — a variant selector on one CRUD page is sufficient; exercising only one variant (for example income-only) is not.
8. Add seed fixtures when demo data helps field coverage (narrative canon and/or a full-detail probe contact); upsert by a stable natural key when re-running should refresh existing records.
9. Only model wire fields that appear in the official FreeAgent API docs.

Update [`samples/README.md`](../../samples/README.md) and [`docs/reference/api-coverage.md`](../../docs/reference/api-coverage.md) in the same change.

## Step 7 — Documentation

Update the root `README.md`, [`src/FreeAgent.Client/README.md`](../../src/FreeAgent.Client/README.md) (API coverage, usage examples), [`docs/reference/api-coverage.md`](../../docs/reference/api-coverage.md), and any affected plan or entity-map sequencing docs.

## Step 8 — Validation

Run from repository root:

```bash
dotnet build
dotnet test
```

Highlight any breaking changes applied during retrofit (DateTime → DateOnly, string → enum).

## References

- `adr/adr-0010-documented-operations-to-sdk-methods.md`
- `docs/contributing/sample-probe-pages.md`
- `plan/IMPLEMENTING_ENDPOINTS.md`
- `plan/API_TYPE_MAPPING_POLICY.md`
- `plan/API_TO_SDK_ALIGNMENT.md`
- `CONVENTIONS.md`
- `AGENTS.md`

