Closient’s local-first search depends on accurate, fresh in-store inventory. This skill takes a retailer Organization from “signed up” to “discoverable in local search” — POS connected, first batch ofDocumentation Index
Fetch the complete documentation index at: https://docs.closient.com/llms.txt
Use this file to discover all available pages before exploring further.
InStoreOffer records synced, and at least one Location published
with PostGIS coordinates so distance-ranked search can find them.
When to use
- A retailer just signed up and needs to be onboarded.
- The agent is helping a retailer add their second / third store location after the first connect.
- A retailer’s existing POS integration broke and the connection needs re-establishing.
Auth
OAuth token (or API key) withintegrations:write, offers:write, and
locations:write scopes. The caller must hold OWNER or MANAGER
role on the retailer Organization.
Flow
Step 1 — Pick a POS integration
Closient supports Shopify today, with Square, Clover, and Lightspeed on the roadmap (see Known gaps). The agent should ask the retailer which POS they use; if it’s not Shopify, route them to the support flow rather than mid-onboarding.Step 2 — Connect via OAuth
Shopify connect is a non-API browser flow today (the OAuth redirect cannot complete inside an agent-only context):InventorySource row (in the retailers app, not integrations —
see apps/integrations/models.py for the rationale). The agent’s
job here is to:
- Generate the install URL with the user’s shop domain.
- Hand the URL to the user (or surface it as a clickable link).
- Wait for the user to confirm completion, then verify via Step 3.
Step 3 — Verify the connection
count == 0 after the user completed
the OAuth handshake, the connect didn’t land — re-issue Step 2 with
a fresh state token.
Step 4 — Sync first batch of inventory
Shopify inventory sync is automatic on connection (background task inapps/integrations/shopify/). The agent should poll for results:
(product_id, physical_store_id, sku) triple is unique — duplicates
return 422.
Step 5 — Publish the first Location
Local search ranks by great-circle distance using PostGIS, so a Location with accuratelat/lon is required for the retailer to
appear in nearby-store queries. Today this is done via the dashboard
UI (/organizations/create and the org-scoped location form) — there
is no POST /locations/api/v1/locations endpoint yet (see Known
gaps). The agent should:
- Direct the user to the dashboard location form.
- Once published, verify via the public endpoint:
distance_km near zero.
Step 6 — Confirm discoverability
Location rows for the org — re-verify Step 5.
Required inputs
organization_id— the retailer’s organization.shop_domain—<retailer>.myshopify.com(Shopify-only today).- One physical store address (city, state/region, postal code) for Step 5.
Optional inputs
latitude/longitude— if the retailer already knows them; else the dashboard form geocodes from the address.- Store hours — populated via the dashboard location form.
- Webhook endpoints — provisioned automatically; can be listed via
GET /integrations/api/v1/webhooks/endpoints/.
Output
integration_status—connectedonce Step 3 verifies;pendingwhile waiting for OAuth completion.synced_offer_count— from the count returned by the in-store-offers list endpoint in Step 4.first_location_url— the public location URL once Step 5 completes (form path:/marketplace/<org_short_id>/locations/<location_short_id>).
Guidance for agents
- Surface the dashboard handoff explicitly. Steps 2 (OAuth) and 5 (Location create) are not fully API-driven today — the user has to click a link / fill a form. Be honest about this rather than pretending the agent can complete them headlessly.
- Don’t poll faster than 5 seconds on the in-store-offers list — Shopify’s catalog sync is rate-limited and we throttle the public API at 300 req/min/key. A patient backoff is more user-friendly than flooding the API.
- One Location at minimum, but encourage all of them. Local search ranks by proximity per-location, so a multi-store retailer that only publishes one location is invisible to the other neighborhoods.
- Inventory freshness matters more than completeness. Surface the
last_inventory_syncfield on offers so the user knows their data is current — stale inventory ranks lower in search. - Webhook secret. The OAuth flow provisions endpoints with a
rotating secret. If the retailer needs to verify webhook deliveries
on their side, the secret is available via
GET /integrations/api/v1/webhooks/endpoints/{endpoint_id}and can be rotated viaPOST /webhooks/endpoints/{endpoint_id}/rotate-secret/.
Known gaps (planned)
- Shopify-only today. The original ticket scope mentions Square,
Clover, and Lightspeed. Models reference these as future
InventorySourcetypes but no OAuth flow exists. Planned per app inapps/integrations/— track via the integrations roadmap. - No headless POS-connect endpoint. Step 2 requires a browser redirect today. A device-code OAuth flow (so agents can complete the connect on behalf of a user with a one-time code) is planned.
- No
POST /locations/api/v1/locations. Step 5 is currently a dashboard form. A first-class Location create endpoint (with PostGIS coordinates, hours, contact info) is planned so this skill can be fully API-driven end-to-end. - No “is my store discoverable?” health endpoint. Step 6 uses the search endpoint as an indirect probe. A dedicated readiness check (POS connected + ≥1 location published + ≥1 in-stock offer) would be cleaner.
- No sync-job status endpoint. The agent has to poll the offer
list to infer sync progress. A
GET /integrations/api/v1/jobs/{job_id}is planned.
Related skills
claim-brand— the parallel flow for brand-owner Organizations.local-product-search— the surface this onboarding enables — what consumers see once the retailer is live.check-product-availability— how agents probe a specific GTIN against the retailer’s now-synced offers.brand-retail-footprint— once a retailer is live, this skill (called from a brand owner’s side) shows their products being carried.