NinjaAPI instance, mounted at /{app}/api/v1/, with its own OpenAPI
3.1 document at /{app}/api/v1/openapi.json. This skill is the map
that gets an agent from “which app owns this concept?” to “this is
the endpoint I want to hit” without grepping templates.
When to use
- An agent knows what data it needs but not which Closient app owns that data.
- A developer is writing an SDK or MCP server and needs to enumerate every API surface.
- The agent hit a
404 Not Foundand wants to confirm the endpoint path before assuming the API is broken.
closient-api-quickstart first.
Discover everything from the API catalog
The canonical enumeration is the RFC 9727 linkset at/.well-known/api-catalog:
anchor— the API’s base URL (e.g.https://www.closient.com/products/api/v1/).service-desc[0].href— the OpenAPI document URL.service-desc[0].type—application/openapi+json.
APP_API_REGISTRY in
backend/apps/core/api/factory.py — that’s the source of truth if you’re
inside the monorepo.
App-by-app — what each API owns
The list is canonical at
backend/apps/core/api/factory.py:APP_API_REGISTRY.
When that list changes, the catalog updates automatically; you don’t
need to re-grep.
Find an endpoint across the 22 APIs
Three strategies, fastest first:1. Browse Mintlify docs
Interactive HTML docs aggregate every per-app OpenAPI spec athttps://docs.closient.com. Search the sidebar by resource name —
“product”, “brand”, “offer”, “recall” — and you’ll get the canonical
endpoint URL with auth, parameters, and response schemas.
2. Fetch the OpenAPI doc directly
For machine consumption (SDK generation, MCP tool registration), grab the per-app spec straight from the catalog and parse it:3. Search the linkset
The catalog itself is linkset-shaped, so you can grep throughanchor and service-desc.href to find candidate apps before fetching
their specs. Useful in an MCP / tool-use context where dropping every
spec into a prompt is wasteful.
Cross-cutting conventions (apply to every per-app API)
Authentication
Every API accepts the same three auth mechanisms; pick one:
Endpoints that need no auth (catalog search, public resolver, well-known
endpoints) declare
auth=None in the OpenAPI spec and accept anonymous
calls subject to a tighter IP-based throttle.
Pagination
List endpoints use limit/offset pagination (cursor pagination is NOT in use):limit=25, max limit=100. Implemented by the shared
apps.core.api.pagination.LimitOffsetPagination.
Error envelope (RFC 9457 Problem Details)
All errors returnapplication/problem+json:
type is a stable URI (you can build clients that switch on it);
title is human-readable; detail may include user-context
information. Wired by apps.core.api.exceptions.register_error_handlers.
Rate-limit headers
Every response carries IETFRateLimit-* headers:
Legacy
X-RateLimit-* aliases are also emitted; X-RateLimit-Reset
keeps the absolute Unix-timestamp shape for back-compat. Implemented in
apps.core.api.throttling. Default: 300/min, 10k/day per key.
Idempotency
Mutating endpoints acceptIdempotency-Key headers where the operation
is naturally idempotent (POSTs that look like upserts, batch ingest
endpoints). The key is opaque to Closient; same key + same body returns
the original response. Check the per-endpoint OpenAPI description for
the explicit Idempotency-Key parameter declaration before relying on
it — not every POST is idempotent.
Empathy contract
EveryField() in every per-app schema carries a description= (or
auto-pipes from the model’s help_text via ModelDocumentedSchema),
and enum-shaped names are typed as StrEnums with the values declared
on the schema. SDK generators get a real enum constraint instead of a
bare string. See backend/apps/core/CLAUDE.md “API Empathy” for the
internal contract.
OpenAPI specs are versioned per app
When an app evolves a breaking change, expect a/api/v2/ mount
alongside /api/v1/. Today only EPCIS 2.0 lives outside the v1
convention (it sits at /api/2.0/ to match the GS1 EPCIS spec naming).
There is no global /v1/.
Cross-app patterns to recognise
”List then detail”
For most resources, list endpoints return a slim representation; detail endpoints return the full one:Short IDs in URLs
Public-facing resources use a 22-characterShortUUIDField for both
PKs and URL paths (see apps.core.fields.ShortUUIDField):
Scoped to organization
Resources owned by an organization (Products, Brands, Offers, Locations, EPCIS events, …) are filtered by the caller’s organization membership. Cross-org reads return404 Not Found rather than 403, to avoid
leaking existence.
Webhooks vs polling
Closient prefers webhooks over polling for state changes. Subscribe via/notifications/api/v1/webhooks (signed with HMAC-SHA256). Endpoints
that emit events are documented in each per-app OpenAPI spec; common
ones: scanner events, recall notices, offer changes, EPCIS captures.
Putting it all together
A typical agent boot sequence:- Hit
/.well-known/api-catalogto learn what’s available. - Hit
/.well-known/openid-configurationto learn how to auth. - Run
closient-api-quickstartto register / authenticate. GET /account/api/v1/whoamito confirm the token’s identity.- Walk per-app OpenAPI specs from the catalog to bind tools.
local-product-search,
resolve-gtin, claim-brand, capture-epcis-event, …) tell the
agent what to do with them.
Related skills
closient-api-quickstart— auth, scopes, first call.decode-gs1-ai,build-gs1-digital-link— GS1 identifier mechanics.local-product-search,resolve-gtin,check-product-availability— search + product depth.claim-brand,onboard-retailer— onboarding workflows.capture-epcis-event,query-epcis-events,manage-recall,check-fsma-204-readiness— supply-chain and compliance.brand-scan-summary,brand-search-demand,brand-retail-footprint— brand analytics.
Authoritative references
- RFC 9727 — API Catalog / Linkset.
https://datatracker.ietf.org/doc/html/rfc9727 - RFC 9457 — Problem Details for HTTP APIs.
- IETF
RateLimit-*headers (informed governor draft). - OpenAPI 3.1 spec — generated docs at
https://docs.closient.com. - Closient internal:
backend/apps/core/api/factory.py,backend/apps/core/api/pagination.py,backend/apps/core/api/throttling.py,backend/apps/core/api/exceptions.py.