Documentation Index
Fetch the complete documentation index at: https://docs.closient.com/llms.txt
Use this file to discover all available pages before exploring further.
Closient is API-first and per-app. Each Django app owns its own
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 Found and wants to confirm the endpoint
path before assuming the API is broken.
For zero-to-first-call setup (auth, scopes, PKCE), run
closient-api-quickstart first.
Discover everything from the API catalog
The canonical enumeration is the RFC 9727 linkset at
/.well-known/api-catalog:
GET /.well-known/api-catalog
Accept: application/linkset+json
Each entry has:
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.
Programmatically:
import httpx
catalog = httpx.get("https://www.closient.com/.well-known/api-catalog").json()
for entry in catalog["linkset"]:
print(entry["anchor"], entry["service-desc"][0]["href"])
The catalog is generated from 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
| Mount | Label | Owns |
|---|
/account/api/v1/ | Account | Users, organizations, API keys, RBAC, OAuth-connected apps, whoami. Start here to identify the caller. |
/agent/api/v1/ | Agent | LLM-powered conversational endpoints (chat, search refinement, tool-use orchestration). |
/advertising/api/v1/ | Advertising | Sponsored placements, impressions, revenue-share reporting. |
/analytics/api/v1/ | Analytics | Scan events, search performance snapshots, brand engagement summaries. Read-heavy. |
/billing/api/v1/ | Billing | Stripe subscription state, usage metrics, plan + tier endpoints. Read-only for non-owners. |
/brands/api/v1/ | Brands | Brand records, ownership/claim flow, brand-level analytics. Write requires brands:write. |
/byos/ | BYOS | ”Bring your own search” — embedded search widget endpoints (not v1 prefixed by convention). |
/certifications/api/v1/ | Certifications | Product certifications (organic, gluten-free, Fair Trade, …). |
/compliance/api/v1/ | Compliance | Recalls, safety notices, FSMA 204 traceability (sibling to /epcis/api/2.0/). |
/content/api/v1/ | Content | Wagtail-backed pages, blog posts, feeds, legal pages. |
/dashboard/api/v1/ | Dashboard | Aggregated views consumed by the Closient web UI. Not a public-facing API. |
/epcis/api/2.0/ | EPCIS 2.0 | GS1 EPCIS 2.0 event capture + query. Path mounted at /api/2.0/ per spec, not /v1/. |
/integrations/api/v1/ | Integrations | POS connections (Shopify, Square, Clover, Lightspeed), webhooks. |
/locations/api/v1/ | Locations | Physical store/service locations with PostGIS geometry. |
/notifications/api/v1/ | Notifications | Multi-channel notification delivery (email, Slack, SMS). |
/products/api/v1/ | Products | Products (GTINs), images, validators, catalog API, categories, QR rendering. |
/resolver/api/v1/ | Resolver | GS1 Digital Link resolution + linkset queries + serial verification. |
/retailers/api/v1/ | Retailers | Offers (in-store + online), inventory sources, online-retailer integrations. |
/scanner/api/v1/ | Scanner | Barcode + product scanning, vision-extracted attributes. |
/search/api/v1/ | Search | Catalog search, session-based local-first search, refinement. |
/sustainability/api/v1/ | Sustainability | Sustainability certifications + impact tracking. |
/voice-feedback/api/v1/ | Voice Feedback | Speech-to-text user feedback + reproducer playback. |
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 at
https://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:
spec = httpx.get(
"https://www.closient.com/products/api/v1/openapi.json"
).json()
for path, methods in spec["paths"].items():
for verb, op in methods.items():
print(verb.upper(), path, "—", op.get("summary"))
Every per-app spec follows the same shape (Django Ninja → OpenAPI 3.1).
3. Search the linkset
The catalog itself is linkset-shaped, so you can grep through
anchor 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:
| Mechanism | Header / cookie |
|---|
| API key | X-API-Key: csb_<body>_<crc32> |
| OAuth 2.0 access token | Authorization: Bearer <token> |
| Session (browser only) | Django sessionid cookie |
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.
List endpoints use limit/offset pagination (cursor pagination is
NOT in use):
GET /products/api/v1/products?limit=50&offset=100
Response envelope:
{
"items": [...],
"count": 50,
"total": 1234,
"limit": 50,
"offset": 100
}
Defaults: limit=25, max limit=100. Implemented by the shared
apps.core.api.pagination.LimitOffsetPagination.
Error envelope (RFC 9457 Problem Details)
All errors return application/problem+json:
{
"type": "https://www.closient.com/problems/forbidden",
"title": "Forbidden",
"status": 403,
"detail": "User does not have brands:write scope on this token.",
"instance": "/brands/api/v1/brands/abc123"
}
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.
Every response carries IETF RateLimit-* headers:
| Header | Value |
|---|
RateLimit-Policy | All active windows, e.g. 300;w=60, 10000;w=86400. |
RateLimit-Limit | Quota for the most-restrictive currently-active window. |
RateLimit-Remaining | Requests left in that window. |
RateLimit-Reset | Seconds until that window resets (relative; clock-skew safe). |
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 accept Idempotency-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
Every Field() 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:
GET /products/api/v1/products → ProductListItem (slim)
GET /products/api/v1/products/{id} → ProductDetail (full)
Don’t filter the list response client-side hoping to find a field —
fetch the detail endpoint or use the documented query parameters.
Short IDs in URLs
Public-facing resources use a 22-character ShortUUIDField for both
PKs and URL paths (see apps.core.fields.ShortUUIDField):
GET /brands/api/v1/brands/4Vh87cAnSyrSt5KQ8s9hkF
Short IDs are accepted in both shortuuid and full-UUID form; the URL
converter normalizes. New code emits the short form.
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 return 404 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-catalog to learn what’s available.
- Hit
/.well-known/openid-configuration to learn how to auth.
- Run
closient-api-quickstart to register / authenticate.
GET /account/api/v1/whoami to confirm the token’s identity.
- Walk per-app OpenAPI specs from the catalog to bind tools.
Once tools are bound, the per-workflow skills (local-product-search,
resolve-gtin, claim-brand, capture-epcis-event, …) tell the
agent what to do with them.
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.