HttpClient, so it fits
into an existing DI container, Polly pipeline, or test harness without extra work.
Every method returns CMSResponse<T>, with T the model that endpoint sends — so
GetByIdAsync hands back CMSResponse<ArticleWrapper> and res.Data.Article.Slug is
a typed field read, not a JsonElement walk.
Install
Initialize
CMSOptions is init-only, so it can only be set at construction. The key is sent as
the x-api-key header on every request; there is no header option, by design.
Make a request
Filters go in as aDictionary<string, string?> — the client URL-encodes them:
A single entity arrives under a key of its own, so it is
res.Data.Article /
res.Data.Author / res.Data.Form, never the model directly. Endpoints that answer
with a free-form payload — every delete, the archive, the streaming AI calls — use
JsonElement for Data.Write operations
The body is anobject, serialized with System.Text.Json, so a plain
Dictionary<string, object> works:
mainTitle, content and category are required to create; author
defaults to the authenticated user. UpdateAsync takes the whole document in
its body — there is no separate id argument, so id is a field of that body.
Error handling
Errors are return values, not exceptions, unless you opt in:CMSError carries only the status and the message. For the unparsed response body,
opt in to throwing:
Timeouts
CMSOptions.Timeout applies to every request — it is set on the underlying
HttpClient, and defaults to that client’s own 100 seconds when left unset. There is
no per-request override.
Escaping the resource wrapper
cms.Raw is the underlying HTTP client, for an endpoint the resources do not cover.
The untyped overload hands back a JsonElement; the generic one names the model:
Resources
Article, Author, Category, Subcategory, Form, FormSubmission, Language,
Tag, Report, AI, AIConversation, Raw.
All operations
Browse the API reference for the full endpoint list.

