JENNi is the inventory network connecting local inventory to demand across America. It enables retailers, brands, and reps to make any stocked item instantly sellable across channels, offering real‑time routing, integration with POS systems like Shopify, Square, and WooCommerce, and tools for supply and demand management. The platform provides a unified view of inventory, supports on‑boarding for partners such as Uber and DoorDash, and includes features for analytics, AI agents, and comprehensive help resources.
JENNi publishes 12 API contracts indexed on the APIs.io network, including Account API, Brand portal API, Catalog API, and 9 more. Tagged areas include Inventory, Retail, Supply Chain, and Marketplace.
The JENNi catalog on APIs.io includes 1 event-driven AsyncAPI specification, 1 JSON-LD context, and 1 Spectral governance ruleset.
JENNi’s developer surface includes changelog, authentication, getting-started guide, API reference, documentation, and 23 more developer resources.
Regulatory Posture applies to this provider. Its tags matched the
Horizontal (data, software, accessibility, platform) regime, so
Regulatory Posture carries 15 points of the composite.
If this regime is wrong for your business, say so on your
provider repo — the
applicability map is public and we will correct it.
Create-or-Update Ergonomics could not be measured. We hold no machine-readable contract for
this provider to read, so there is nothing to measure a write surface against. Excluded rather than scored zero:
never-measured and measured-empty are different facts. Publishing an OpenAPI is what makes this facet — and
several others — scorable at all.
The six quality facets above are damped to 85 points between them,
because the conditional facet above carries the other
15. That is why each facet's contribution is shown against a damped
maximum: raising a quality facet moves the composite by 85% of its nominal
weight, not 100%. The full arithmetic is at apis.io/rating/.
Show this score on your site.
It updates itself when the score changes. Paste this where you link to your API docs:
<a href="https://apis.io/providers/jennipro-com/"><img src="https://apis.io/badge/jennipro-com/card.svg" alt="JENNi on APIs.io — Kin Score by API Evangelist"></a>
Other styles, a README pill and a shields.io endpoint are on the badge page.
Raise your score.
Agent readiness — checks not met, largest first (exact, from this profile):
MCP Server+12 pts — deployment.mode + probe verdict on the mcp/ manifest, not the pointer; an unprobed slug falls back to authorship Ask about this →
Delegated User Identity+6 pts — a served discovery document listing authorization_code in grant_types_supported (served), else an oauth2 authorizationCode flow or openIdConnect declared in an OpenAPI (documented) Ask about this →
Protected Resource Metadata+5 pts — a served /.well-known/oauth-protected-resource carrying `resource` (documented), and naming its authorization_servers (verified) Ask about this →
4 more unmet checks in the Agent Readiness table above.
Kin Score — facets with the most composite points still available (weight × headroom):
Each check links to what satisfies it. Publish the artifact, or tell us where it already lives, and the score moves on the next scoring run.
Not sure how this applies to your stack? Ask about any one item. Kin Lane reads every question, looks at your profile and your situation, and replies with what a fix would take — scoped to that one area, priced up front, yours whether you ship it or not. Ask a question →
Standards this provider implements or carries. De facto ones are profiled and measured by the API Commons; formal ones come from the standards directory and are linked by the provider's own tags. The evidence column says how each claim was established — not that it was made.
What this key is. Authentication-only: a key with no provider scope still gets an answer here, so the portal and the partner can see why everything else 403s.
What a signed-in BRAND user sees at the brands host: their brand profile (name, accent colour, site URL), publishable widget keys bound to their origins, a coverage preview of t...
Read a store's shelf as a demand channel. The mirror image of the ingestion surface above: /provider/v1 is how a retailer writes its catalog IN, /channel/v1 is how a channel rea...
The partner self-service portal: what a signed-in partner USER does with their account — see who they are and which keys their tenant holds, ask JENNi for a key, more scope, pro...
## Bring a retailer onto JENNi A **retailer** is a business you are bringing onto JENNi — the merchant whose stores you will push inventory for. Registering it gives the merchan...
Open, inspect and close runs. Every write belongs to a run, and a run is how JENNi attributes what you sent and (in snapshot mode) what to remove. ### How every ingestion integr...
## Manage your stores A **store** is a physical or virtual location you sell from. Every inventory level you push is written *to a store*, so a store must be registered before i...
Signed HTTPS callbacks for the retailers you provision: `retailer.created`, `retailer.claim_link.issued`, `retailer.claim_link.consumed`, `retailer.claimed`, and `run.closed` wh...
aid: jennipro-com
name: JENNi
accessModel:
pricing: unknown
onboarding: unknown
trial: false
try_now: false
public: false
label: Unknown
confidence: low
probe_state: unreadable
source:
- authentication
- rate-limits
- security
generated: '2026-11-13'
method: derived
image: https://jennipro.com/og-default.png
description: JENNi is the inventory network connecting local inventory to demand across America. It enables retailers, brands,
and reps to make any stocked item instantly sellable across channels, offering real‑time routing, integration with POS systems
like Shopify, Square, and WooCommerce, and tools for supply and demand management. The platform provides a unified view
of inventory, supports on‑boarding for partners such as Uber and DoorDash, and includes features for analytics, AI agents,
and comprehensive help resources.
url: https://artifacts.apis.io/jennipro-com/apis.yml
x-type: company
x-source: harvest:mcp-registry
x-tier: profiled
x-tier-reason: enrichment pass 2026-11-13 (modular-v1; was harvest)
specificationVersion: '0.20'
created: '2026-11-12'
modified: '2026-11-12'
tags:
- Inventory
- Retail
- Supply Chain
- Marketplace
tags_raw:
- Inventory
- Retail
- SupplyChain
- Marketplace
- API
maintainers:
- FN: Kin Lane
email: kin@apievangelist.com
- FN: APIs.json
email: info@apis.io
common:
- type: AgenticAccess
url: agentic-access/jennipro-com-agentic-access.yml
- type: RateLimits
url: rate-limits/jennipro-com-rate-limits.yml
- type: Spectral
url: rules/jennipro-com-rules.yml
- type: JSONLD
url: json-ld/jennipro-com-context.jsonld
- type: Vocabulary
url: vocabulary/jennipro-com-vocabulary.yml
- type: AgentSkill
url: skills/_index.yml
- type: Webhooks
url: asyncapi/jennipro-com-webhooks.yml
- type: DataModel
url: data-model/jennipro-com-data-model.yml
- type: ChangeLog
url: changelog/jennipro-com-changelog.yml
- type: Idempotency
url: conventions/jennipro-com-conventions.yml
- type: Conventions
url: conventions/jennipro-com-conventions.yml
- type: Authentication
url: authentication/jennipro-com-authentication.yml
- type: Deprecation
url: lifecycle/jennipro-com-lifecycle.yml
- type: Lifecycle
url: lifecycle/jennipro-com-lifecycle.yml
- type: ErrorCatalog
url: errors/jennipro-com-problem-types.yml
- type: Conformance
url: conformance/jennipro-com-conformance.yml
- type: LLMsTxt
url: llms/jennipro-com-llms.txt
- type: SecurityTxt
url: well-known/jennipro-com-developer-security.txt
- type: WellKnown
url: well-known/jennipro-com-well-known.yml
- type: Hosts
url: hosts/jennipro-com-hosts.yml
- type: Leadership
url: https://jennipro.com/team
- type: GettingStarted
url: https://developer.jennipro.com/#tag/quickstart
- type: APIReference
url: https://developer.jennipro.com/
- type: DomainSecurity
url: security/jennipro-com-domain-security.yml
- type: Website
url: https://jennipro.com/
- type: Documentation
url: https://jennipro.com/help
- type: TermsOfService
url: https://jennipro.com/terms
- type: PrivacyPolicy
url: https://jennipro.com/privacy
apis:
- aid: jennipro-com:jennipro-com-account-api
name: JENNi Account API
description: 'What this key is. Authentication-only: a key with no provider scope still gets an answer here, so the portal
and the partner can see why everything else 403s.'
baseURL: https://prod.jennipro.net
tags:
- Account
properties:
- type: OpenAPI
url: openapi/jennipro-com-account-api-openapi.yml
- aid: jennipro-com:jennipro-com-brand-portal-api
name: JENNi Brand portal API
description: 'What a signed-in BRAND user sees at the brands host: their brand profile (name, accent colour, site URL),
publishable widget keys bound to their origins, a coverage preview of their site, shopper demand by 3-digit ZIP prefix,
and the orders their widget tag produced. Called with a partner access token (`Authorization: Bearer …`), never an API
key. The tenant is always the signed-in account: a tenant in a query or body is ignored. provider-bff forwards profile
and key calls to identity-service, coverage and demand to product-availability-service and orders to order-j; every body
is re-mapped through a fixed allow-list, so no shopper name, e-mail, phone, street or full postal code is ever returned.
Ships dark behind `ENABLE_BRAND_PORTAL`.'
baseURL: https://prod.jennipro.net
tags:
- Brand portal
properties:
- type: OpenAPI
url: openapi/jennipro-com-brand-portal-api-openapi.yml
- aid: jennipro-com:jennipro-com-catalog-api
name: JENNi Catalog API
description: Push and read back product records.
baseURL: https://prod.jennipro.net
tags:
- Catalog
properties:
- type: OpenAPI
url: openapi/jennipro-com-catalog-api-openapi.yml
- aid: jennipro-com:jennipro-com-channel-api
name: JENNi Channel API
description: 'Read a store''s shelf as a demand channel. The mirror image of the ingestion surface above: /provider/v1 is
how a retailer writes its catalog IN, /channel/v1 is how a channel reads one store''s catalog OUT.
### The channel surface (`/channel/v1`)
If you are a demand CHANNEL rather than a retailer, the ingestion flow above is
not yours. `/channel/v1` is read-only and store-scoped: `GET /channel/v1/stores`
tells you which stores your credential may read, and the catalog and inventory
reads under each store return that store''s shelf. A store you are not bound to
is a `403`.
These routes are disabled by default in source. JENNi enables them per
environment and binds each channel credential to the stores it may read. Confirm
the target environment and store bindings before depending on this surface;
when disabled, the routes are not registered and return `404`.'
baseURL: https://prod.jennipro.net
tags:
- Channels
tags_raw:
- Channel
properties:
- type: OpenAPI
url: openapi/jennipro-com-channel-api-openapi.yml
- aid: jennipro-com:jennipro-com-events-api
name: JENNi Events API
description: Look up the delivery status of a single previously submitted item.
baseURL: https://prod.jennipro.net
tags:
- Event
tags_raw:
- Events
properties:
- type: OpenAPI
url: openapi/jennipro-com-events-api-openapi.yml
- aid: jennipro-com:jennipro-com-inventory-api
name: JENNi Inventory API
description: Push stock levels and prices. This is the highest-volume surface and the one most integrations start with.
baseURL: https://prod.jennipro.net
tags:
- Inventory
properties:
- type: OpenAPI
url: openapi/jennipro-com-inventory-api-openapi.yml
- aid: jennipro-com:jennipro-com-portal-api
name: JENNi Portal API
description: 'The partner self-service portal: what a signed-in partner USER does with their account — see who they are
and which keys their tenant holds, ask JENNi for a key, more scope, production access or help, follow those requests,
and claim an approved key (the one moment its secret is shown). Where the deployment advertises `self_serve.keys` (`GET
/provider/v1/portal/me`), a sandbox tenant also mints its own keys (`POST /provider/v1/portal/keys`) and rotates or revokes
them (`POST /provider/v1/portal/keys/{id}/transitions`) without an operator; `GET /provider/v1/portal/health` is the integration
card — tenant, key summary and recent ingestion in one read that degrades section by section instead of failing. These
routes are called with a partner access token (`Authorization: Bearer …`, obtained from identity-service''s public `POST
/v1/partner/auth/login`), never with an API key: a key on a portal route is a `403`, and a partner token on a key route
is a `401`. provider-bff forwards each call to identity-service, which owns partner accounts, requests, grants and keys;
identity''s own refusal comes back with its status and problem `type`. Ships dark behind `ENABLE_PORTAL_ROUTES`.'
baseURL: https://prod.jennipro.net
tags:
- Portal
properties:
- type: OpenAPI
url: openapi/jennipro-com-portal-api-openapi.yml
- aid: jennipro-com:jennipro-com-retailers-api
name: JENNi Retailers API
description: "## Bring a retailer onto JENNi\n\nA **retailer** is a business you are bringing onto JENNi — the merchant\
\ whose\nstores you will push inventory for. Registering it gives the merchant a place\nin the JENNi Console to claim;\
\ until they do, the retailer exists but has no\nowner. You register, you deliver the claim link, you can watch the claim\
\ land.\n\n### The flow\n\n1. `POST /provider/v1/retailers` with **your** `external_retailer_id` and\n the retailer's\
\ name — `201` with a `retailer_id`. Store it. Registering\n the same `external_retailer_id` again is a `200` with the\
\ same retailer,\n never a second one.\n2. `POST /provider/v1/retailers/{retailer_id}/claim-links` — `201` with a\n\
\ `url` of the form `{CLAIM_URL_BASE}/claim?r=<retailer_id>&t=<token>`:\n `r` names the retailer being claimed and\
\ `t` is a single-use secret that\n expires at `expires_at` (7 days unless you shorten it). **The URL is shown\n once**\
\ — JENNi keeps only a hash of the token. Deliver it to the merchant\n yourself (JENNi sends nothing); if it is lost,\
\ issue a new link, which\n supersedes the old one.\n3. `GET /provider/v1/retailers/{retailer_id}` — `claim.status`\
\ moves from\n `provisioned` to `invited` (a live link exists) to `claimed` (the\n merchant signed in and owns it),\
\ with `claimed_at` and a secret-free summary\n of the latest link. Poll it, or show it to your users as onboarding\
\ progress.\n4. When you register the retailer's stores with `POST /provider/v1/stores`,\n send the `retailer_id` so\
\ JENNi can tie the store to the merchant who\n claimed it.\n\n### Which key\n\nReads (`GET`) accept a read, write or\
\ ingest key. The two `POST`s need a\nwrite or ingest key and an `Idempotency-Key` header. Everything is scoped to\nthe\
\ provider on your key: no route takes a `provider_id`, and a retailer\nanother provider registered is a `404 retailer-not-found`\
\ — exactly the\nresponse for an id that was never issued.\n\n### Finding a retailer again\n\n`GET /provider/v1/retailers?external_retailer_id=…`\
\ returns the retailer you\nregistered under that id (or an empty page). A paged listing of all your\nretailers, with\
\ `claim_status`, `cursor` and `limit`, is planned on the same\nroute and will not break this call.\n\n### Availability\n\
\nThis surface is rolling out per environment. Where it is not enabled yet every\ncall answers `503 retailers-unavailable`\
\ — retryable, and never a `404` you\ncould mistake for a typo. Everything else in this API works regardless."
baseURL: https://prod.jennipro.net
tags:
- Retailers
properties:
- type: OpenAPI
url: openapi/jennipro-com-retailers-api-openapi.yml
- aid: jennipro-com:jennipro-com-runs-api
name: JENNi Runs API
description: "Open, inspect and close runs. Every write belongs to a run, and a run is how JENNi attributes what you sent\
\ and (in snapshot mode) what to remove.\n\n### How every ingestion integration is shaped\n\nWrite your catalog and inventory\
\ into the JENNi product graph, then reconcile what\nJENNi holds against what you sent.\n\n## The shape of every integration\n\
\nWork is always scoped to a **run**. A run is the unit of source attribution, batch\nidempotency and — in snapshot mode\
\ — the full-replace window.\n\n1. `POST /provider/v1/stores` — register each store once. (Bringing a new\n business\
\ onto JENNi? Register it as a **retailer** first — see the Retailers\n guide — and pass its `retailer_id` here.)\n\
2. `POST /provider/v1/runs` — open a run.\n3. Push batches against that run (`/inventory/levels/bulk`, `/products/bulk`,\n\
\ or `/inventory/snapshots`).\n4. `POST /provider/v1/runs/{run_id}/close` — close it.\n5. `GET /provider/v1/products`\
\ / `GET /provider/v1/inventory/levels` — read back\n what landed.\n\nOnly an **open** run accepts writes; a batch against\
\ a closed, expired or failed\nrun is rejected with `409`.\n\n## Two ingestion modes\n\n- **Mode A — incremental** (`mode:\
\ \"incremental\"`, scope `provider.write`).\n Each batch is a delta. Send what changed. Nothing is removed implicitly.\n\
- **Mode B — snapshot** (`mode: \"snapshot\"`, scope `provider.ingest`). The run\n carries the COMPLETE set of levels\
\ for ONE store. Anything absent from the\n snapshot is soft-deleted when the run closes. A snapshot run must declare\
\ its\n `store_id` and `expected_items` when it is opened.\n\n## Partial success is normal\n\nBoth bulk endpoints return\
\ a per-item envelope. **`200` means every item was\naccepted; `207` means at least one was not.** Always walk `results[]`\
\ and join\non `event_id` — a 2xx status alone does not mean every item landed. An item is\n`accepted` (durable), `rejected`\
\ (permanently bad — do not retry unchanged) or\n`deferred` (indeterminate upstream failure — retry it).\n\n## Errors\n\
\nEvery 4xx and 5xx is RFC-7807 `application/problem+json`. Branch on the stable\n`type` URI, never on `title` or `detail`.\
\ Every response carries\n`X-Request-Id`; quote it in support requests.\n\n## Tenancy\n\nYour tenant and your provider\
\ are derived from your API key. There is no tenant\nparameter anywhere in this API — not in a path, not in a query string,\
\ not in a\nbody. A key is scoped to exactly one provider."
baseURL: https://prod.jennipro.net
tags:
- Runs
properties:
- type: OpenAPI
url: openapi/jennipro-com-runs-api-openapi.yml
- aid: jennipro-com:jennipro-com-service-api
name: JENNi Service API
description: Liveness and readiness probes. Internal — not part of the partner contract.
baseURL: https://prod.jennipro.net
tags:
- Services
tags_raw:
- Service
properties:
- type: OpenAPI
url: openapi/jennipro-com-service-api-openapi.yml
- aid: jennipro-com:jennipro-com-stores-api
name: JENNi Stores API
description: '## Manage your stores
A **store** is a physical or virtual location you sell from. Every inventory
level you push is written *to a store*, so a store must be registered before
its first level — a level against an unregistered store is rejected with
`422 unknown-store`. Register with `POST /provider/v1/stores`; afterwards,
read and maintain the store with the routes below.
### Identity
You address a store by **your own** `retailer_store_id` — the one you chose
when you registered it. JENNi assigns a `jenni_store_id` too, but you never
need to send it. A store always belongs to the provider on your API key, which
is why none of the management routes take a `provider_id` (sending one is a
`400`). A store you did not register, or one that has since been deleted or
merged into another, is a `404 store-not-found`.
### Which key
Reads (`GET`) accept a read, write or ingest key. Writes (`PATCH`,
`PUT …/hours`, `POST`) need a write or ingest key. Every write except the
original `POST` requires an `Idempotency-Key` header.
### Details
`PATCH /provider/v1/stores/{retailer_store_id}` changes any subset of the
store''s details — name, address, coordinates, contact, `store_url`,
`logo_url`, `is_active`. Send only the fields you want to change: omitted
fields are untouched, `null` clears a field. `logo_url` must be
`https://`, at most 2048 characters, with a real hostname (never an IP
address). Types are strict: `is_active` is `true` or `false` and cannot be
cleared with `null`; `lat`/`lng` are JSON numbers, never strings. Send
`lat` and `lng` together: both numbers, or both `null` to clear the
location. A lone `lat` or `lng`, a mix of number and `null`, and `0,0`
are refused with a 400. The
response is the full store as it now stands.
### Hours
Hours live in the store''s `hours` block: a `weekly` schedule keyed by the
seven upper-case day names, each a list of up to three `{open, close}`
intervals on a 24-hour `HH:MM` clock (an empty list means closed that day;
`close` earlier than `open` means the store closes after midnight), plus
`special` — dated overrides such as holidays, at most 60, each with a
`closed` flag and an optional `reason`. Each `special` date may appear
only once; a second row for the same day is a `400`, not a merge. Set them with
`PUT /provider/v1/stores/{retailer_store_id}/hours` (or as `hours` inside a
`PATCH`); read them back as the `hours` block of `GET /provider/v1/stores/{retailer_store_id}`
(the separate `GET …/hours` read is deprecated and answers with a `Sunset` header).
`hours.source` tells you who owns them. Once a merchant edits a store''s hours
in the JENNi Console the source becomes `merchant` and **your hours writes are
refused with `409 hours-merchant-managed`** — nothing is applied, including
other fields in the same `PATCH`. Ask the merchant to hand hours back to you,
or leave them; everything else about the store stays yours to update.
The `store_hours` field on `POST /provider/v1/stores` is **deprecated**. It
still works, but only with the same weekly dict `PUT …/hours` takes; any other
shape is a `400` that points you at the new route.'
baseURL: https://prod.jennipro.net
tags:
- Stores
properties:
- type: OpenAPI
url: openapi/jennipro-com-stores-api-openapi.yml
- aid: jennipro-com:jennipro-com-webhooks-api
name: JENNi Webhooks API
description: 'Signed HTTPS callbacks for the retailers you provision: `retailer.created`, `retailer.claim_link.issued`,
`retailer.claim_link.consumed`, `retailer.claimed`, and `run.closed` when one of your runs reaches `closed`, plus `ping`.
A signed-in partner user registers up to 5 endpoints from the portal (`/provider/v1/portal/webhooks`); JENNi POSTs each
event once per endpoint as `{id, type, created_at, data}` with `X-Jenni-Event`, `X-Jenni-Delivery`, `X-Jenni-Timestamp`
and `X-Jenni-Signature: v1=<hex hmac-sha256(secret, "<timestamp>.<body>")>`, retries a non-2xx after 1m, 5m, 30m, 2h and
12h, and pauses an endpoint after 5 deliveries in a row exhaust their retries. Only events for your own provider are delivered.
The signing secret is shown once, at registration or rotation. Full guide: docs/webhooks.md. Ships dark behind `PARTNER_WEBHOOKS_ENABLED`
(on top of `ENABLE_PORTAL_ROUTES`).'
baseURL: https://prod.jennipro.net
tags:
- Webhook
tags_raw:
- Webhooks
properties:
- type: OpenAPI
url: openapi/jennipro-com-webhooks-api-openapi.yml
x-coverage:
state: unreadable
reason: js-rendered-docs
detail: Documentation pages are large HTML shells with no machine‑readable OpenAPI spec.
evidence:
- url: https://jennipro.com/help
status: 200
checked: '2026-11-13'
x-enrichment:
date: '2026-11-13'
status: enriched
artifacts_added: 33
pass: modular-v1
engine: identity=local,contract=local,surfaces=local,docs=local,regulatory=local
JENNi API — frequently asked questions
Does JENNi have an API?
Yes. APIs.io lists 12 APIs from JENNi, including JENNi Account API, JENNi Brand portal API and JENNi Catalog API, and 9 more.
Is there an OpenAPI specification for the JENNi API?
Yes. APIs.io indexes 12 OpenAPI specifications for JENNi, each linked for download from this page.
How do I authenticate with the JENNi API?
JENNi uses bearer-token authentication (1 scheme). The details are profiled on APIs.io as JENNi Authentication.
Is the JENNi API ready for AI agents?
JENNi has a Kin Score of 48.1 out of 100 (developing) and an Agent Readiness score of 46.1 (agent-native), scored by API Evangelist on 2026-11-13.
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.