JENNi Events API

Look up the delivery status of a single previously submitted item.

GitHub OpenAPI

Operations 1

GET /provider/v1/events/{event_id} Get the status of one submitted item #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/jennipro-com:jennipro-com-events-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

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.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

jennipro-com-events-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  contact:
    email: partners@jennipro.com
    name: JENNi partner integrations
  description: 'One API, read by four kinds of partner. **Get started** (left) is the same for everyone:

    sign up, get a key, make five calls. Then pick who you are: each card names the guide to

    read first and the API sections you will actually call, in that order. The sidebar is

    grouped the same way; everything cross-cutting (errors, paging, limits, versioning) sits

    under **Reference** at the end.


    POS systems


    You run the point of sale, inventory or product-data system for retailers, and you push their catalog and live stock into JENNi.


    What you do: register each store, open a run, push products and stock levels (or a full snapshot), close the run, read back what landed.


    Read, then call: POS: catalog & inventory → Stores · Runs · Catalog · Inventory


    First call: POST /provider/v1/stores


    Live in production.


    Brands


    You own the product content — titles, images, descriptions, GTINs — for products that retailers stock, and you want JENNi''s listings to carry your version.


    What you do: send product records through the same ingestion lane a POS uses, then make sure each one clears the readiness bar, because a record that lands but is not ready is never sold.


    Read, then call: Brands → Product readiness → Catalog (listed under POS systems)


    First call: GET /provider/v1/products


    Same lane as POS. Brand-to-retailer matching is handled by JENNi, not by an API call.


    Marketplaces & channels


    You are a demand channel — a marketplace, delivery app or storefront — that lists JENNi merchants'' stock and sends orders back.


    What you do: read one store''s shelf at a time, poll or receive changes, and agree an order path with JENNi (orders in are a JENNi-built adapter, not a public endpoint).


    Read, then call: Marketplace & channel → Channel · Events · Webhooks


    First call: GET /channel/v1/stores


    The read contract is settled and documented; it is enabled per channel — the guide says what is callable today.


    Fulfillment & delivery


    You pick, pack, ship or hand over orders for JENNi merchants.


    What you do: today, plan. The fulfillment service has no partner-callable route; the guide documents what is built so you can scope against it.


    Read: Fulfillment & delivery


    First call: none yet — talk to your JENNi contact.


    Planning only today; talk to your JENNi contact before building.


    ## Before your first call


    - **Get a credential:** *Sign in / Sign up* (top right) → verify your email → *Request a

    key*. JENNi approves it and you claim the secret once — Quickstart walks

    through it.

    - **Base URLs:** `https://prod.jennipro.net` is production; `https://dev.jennipro.app` is the

    sandbox, with its own credentials issued on request.

    - **Credential:** `X-Jenni-Api-Key: .` on every request — see

    Authentication and Scopes. Your tenant and provider come

    from the key; no tenant id ever travels in a path or body.

    - **Errors** are RFC-7807 `application/problem+json`; branch on `type`, quote

    `X-Request-Id` to support — see Errors.

    - **Writes** carry `Idempotency-Key`; lists page with `cursor` + `limit` — see

    Idempotency and Pagination.


    Where a capability is not yet available to partners, the page says so in place.'
  title: JENNi Catalog & Inventory Ingestion Events API
  version: '2026-09-20'
servers:
- description: Production
  url: https://prod.jennipro.net
- description: Development / integration sandbox
  url: https://dev.jennipro.app
security:
- PartnerApiKey: []
tags:
- description: Look up the delivery status of a single previously submitted item.
  name: Events
paths:
  /provider/v1/events/{event_id}:
    get:
      description: 'Looks up what happened to a single item you submitted, by the `event_id` you gave it. Use it to chase an individual item without replaying a whole batch.


        ⚠️ **Only two of the four declared statuses are ever returned today.** An item that JENNi accepted reports `pending` and stays there permanently: nothing downstream currently advances it, so `published` and `consumed` are reserved for a future release and are never emitted. Read `pending` as "accepted by JENNi", NOT as "still being processed". A rejected item reports `failed` with the reason in `failure`.


        An event belonging to another provider returns `404`, exactly like one that does not exist.


        To review a whole run''s outcomes at once, use `GET /provider/v1/runs/{run_id}/items?status=rejected` instead of calling this per item.'
      operationId: getEventStatus
      parameters:
      - in: path
        name: event_id
        required: true
        schema:
          minLength: 1
          type: string
      - description: Your own correlation id. It is echoed back on the response and stamped on the RFC-7807 problem body as `request_id`. Omit it and JENNi generates one.
        in: header
        name: X-Request-Id
        required: false
        schema:
          type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventStatusView'
          description: The item's status.
          headers:
            X-Request-Id:
              description: Correlation id for this request — echoed from X-Request-Id when you sent one, otherwise generated.
              schema:
                type: string
        '400':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
          description: The event_id path parameter failed validation.
          headers:
            X-Request-Id:
              description: Correlation id for this request — echoed from X-Request-Id when you sent one, otherwise generated.
              schema:
                type: string
        '401':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
          description: Authentication failed — the X-Jenni-Api-Key header is missing, malformed, or names a key that is unknown or revoked.
          headers:
            X-Request-Id:
              description: Correlation id for this request — echoed from X-Request-Id when you sent one, otherwise generated.
              schema:
                type: string
        '403':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
          description: The key is valid but not authorized for this provider or this mode. A key carries exactly one provider; snapshot writes additionally require a `provider.ingest` key.
          headers:
            X-Request-Id:
              description: Correlation id for this request — echoed from X-Request-Id when you sent one, otherwise generated.
              schema:
                type: string
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
          description: No such event for your provider.
          headers:
            X-Request-Id:
              description: Correlation id for this request — echoed from X-Request-Id when you sent one, otherwise generated.
              schema:
                type: string
        '429':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
          description: 'Throttled. The per-key request-rate limit is enforced at the edge and answers first: exceeding it returns a `429` from the edge as `application/problem+json` of `type` `rate-limited` (no `Jenni-Version`) with the headers `x-ratelimit-limit`, `x-ratelimit-remaining` and `x-ratelimit-reset` (seconds until the window resets) — no `Retry-After`. A `429` carrying `Jenni-Version` and `Retry-After` was issued by provider-bff''s in-pod fallback limiter behind the edge; its `type` is the same `rate-limited`.'
          headers:
            Retry-After:
              description: 'Seconds to wait before retrying. Present on every `429` provider-bff issues itself (problem+json body: `daily-budget-exhausted`, `sandbox-quota-exceeded`, `rate-limited`); ABSENT on the edge''s per-key rate-limit `429` (problem+json `rate-limited` without `Jenni-Version`) — read `x-ratelimit-reset` there instead. On `409` it is present only for `idempotency-in-progress` (an identical request still in flight) — the other 409s are permanent and carry no Retry-After.'
              schema:
                type: string
            X-Request-Id:
              description: 'Correlation id for this request — same value as `x-jenni-request-id` and the body `request_id`, on every `429`: provider-bff echoes it on the ones it issues and the edge stamps it on its own per-key rate-limit `429`. Quote it when you contact support.'
              schema:
                type: string
            x-ratelimit-limit:
              description: 'Edge per-key rate limit, IETF draft-03 `RateLimit` compound form: the applied limit, then each configured policy as `<n>;w=<window-seconds>` — e.g. `100, 100;w=60, 1000;w=60`. A STRING, not an integer; parse the first number for the limit. Present only on the edge''s `429` (problem+json `rate-limited`, no `Jenni-Version`).'
              schema:
                example: 100, 100;w=60, 1000;w=60
                type: string
            x-ratelimit-remaining:
              description: Requests left in the current window for your key (`0` on the edge `429`). Present only on the edge's `429` (problem+json `rate-limited`, no `Jenni-Version`).
              schema:
                example: '0'
                type: string
            x-ratelimit-reset:
              description: Seconds until the per-key window resets — the value to wait on, since the edge `429` carries no `Retry-After`. Present only on the edge's `429` (problem+json `rate-limited`, no `Jenni-Version`).
              schema:
                example: '4'
                type: string
        '500':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
          description: Unexpected internal error. Safe to retry; quote request_id if it persists.
          headers:
            X-Request-Id:
              description: Correlation id for this request — echoed from X-Request-Id when you sent one, otherwise generated.
              schema:
                type: string
        '503':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
          description: A dependency JENNi needs is unavailable (identity-service, or the catalog write layer). Fail-closed and retryable — retry with backoff.
          headers:
            X-Request-Id:
              description: Correlation id for this request — echoed from X-Request-Id when you sent one, otherwise generated.
              schema:
                type: string
      summary: Get the status of one submitted item
      tags:
      - Events
      x-jenni-modes:
      - write
      - ingest
      - read
      x-required-scope: provider.write:{provider_id}, provider.ingest:{provider_id} or provider.read:{provider_id}
components:
  schemas:
    TrimmedProblem:
      description: The trimmed {type, detail} problem carried on a per-item rejection. Same `type` vocabulary as the full Problem.
      properties:
        detail:
          example: one of upc/gtin is required
          type: string
        type:
          example: https://jenni.com/problem/validation-failed
          format: uri
          type: string
      required:
      - type
      - detail
      title: TrimmedProblem
      type: object
    ProblemError:
      description: One field-level validation failure.
      properties:
        detail:
          example: must be >= 0
          type: string
        field:
          description: Dotted path to the offending field, or '(root)'.
          example: items.0.quantity
          type: string
      required:
      - field
      - detail
      title: ProblemError
      type: object
    Problem:
      description: RFC-7807 problem details. EVERY 4xx and 5xx on this API returns this shape as `application/problem+json`. Branch on the stable `type` URI, never on `title` or `detail` (both are human-facing and may change).
      properties:
        active_key_ids:
          description: 'Extension member on `active-key-cap`: the key ids already active for the tenant, so a client can revoke one without re-listing.'
          example:
          - 2f1c...
          - 9a44...
          items:
            type: string
          type: array
        detail:
          description: What went wrong for THIS request.
          type: string
        errors:
          description: Field-level detail; present on validation failures.
          items:
            $ref: '#/components/schemas/ProblemError'
          type: array
        instance:
          description: The request path that produced the problem.
          type: string
        limit:
          description: 'Extension member on `sandbox-quota-exceeded`: the sandbox tenant''s daily item quota.'
          example: 1000
          type: integer
        request_id:
          description: Correlation id, also echoed in the X-Request-Id response header. Quote it in support requests.
          type: string
        resets_at:
          description: 'Extension member on `sandbox-quota-exceeded`: when `used` resets — the next UTC midnight, RFC 3339.'
          example: '2026-09-17T00:00:00.000Z'
          format: date-time
          type: string
        status:
          description: Mirrors the HTTP status.
          type: integer
        title:
          description: Short human summary. Do not branch on it.
          type: string
        type:
          description: Stable problem-type URI — the machine-readable error identity. See the error catalogue in the API reference.
          example: https://jenni.com/problem/run-not-open
          format: uri
          type: string
        used:
          description: 'Extension member on `sandbox-quota-exceeded`: items already accepted for this sandbox tenant today (UTC). The refused batch is NOT included — it was not applied.'
          example: 800
          type: integer
      required:
      - type
      - title
      - status
      - detail
      - instance
      - request_id
      title: Problem
      type: object
    EventStatusView:
      description: Delivery status for one previously submitted item, looked up by its event_id.
      properties:
        accepted_offset:
          description: Decimal string; see ItemResult.accepted_offset.
          type:
          - 'null'
          - string
        event_id:
          type: string
        failure:
          anyOf:
          - $ref: '#/components/schemas/TrimmedProblem'
          - type: 'null'
          description: Populated only when status=failed.
        last_transition_at:
          format: date-time
          type: string
        status:
          description: 'IN PRACTICE ONLY `pending` AND `failed` ARE RETURNED TODAY. An accepted item reports `pending` indefinitely: no downstream consumer transitions it, so `published`/`consumed` are reserved for a future release and are never emitted. `pending` therefore means ''accepted by JENNi'', NOT ''still in flight''.'
          enum:
          - pending
          - published
          - consumed
          - failed
          type: string
      required:
      - event_id
      - status
      - last_transition_at
      - accepted_offset
      - failure
      title: EventStatusView
      type: object
  securitySchemes:
    PartnerApiKey:
      description: 'Your partner API key, sent as `{keyId}.{secret}` — the key id, one dot, then the secret. The value is split on the FIRST dot only, so a secret that itself contains dots is handled correctly; do not trim or re-encode it. The key determines your tenant and your provider: there is no tenant parameter anywhere in this API. Keys are revoked immediately when rotated, so treat a 401 as final rather than retrying it.'
      in: header
      name: X-Jenni-Api-Key
      type: apiKey
    PartnerBearer:
      bearerFormat: JWT
      description: 'The partner portal session token, sent as `Authorization: Bearer <token>`. It is issued by the portal login (identity-service `/v1/partner/auth/login`) and expires after an hour; refresh it with the refresh token rather than logging in again. It authenticates the `/provider/v1/portal/*` routes ONLY — an API key is refused there with `403 missing-scope`, and this token is refused on every other route with `401 invalid-auth`. The two credentials never substitute for each other.'
      scheme: bearer
      type: http
x-jenni-field-groups:
  core:
    cost: 2
    fields:
    - jenni_product_id
    - jenni_product_store_id
    - jenni_store_id
    - store_id
    - in_stock
    - modified_at
  groups:
  - cost: 1
    fields:
    - gtin
    - upc
    name: identifiers
  - cost: 3
    fields:
    - title
    - brand
    - color
    - size
    - product_link
    name: content
  - cost: 2
    fields:
    - price
    - sale_price
    name: pricing
  - cost: 1
    fields:
    - inventory
    name: availability
  - cost: 1
    fields:
    - sku
    - source_run_id
    name: attribution
x-tagGroups:
- name: Get started
  tags:
  - Quickstart
  - Authentication
  - Scopes
  - Platform basics
  - Sandbox
- name: POS systems
  tags:
  - POS integration brief
  - 'POS: catalog & inventory'
  - Retailers & claim links
  - Retailers
  - Stores
  - Runs
  - Catalog
  - Inventory
- name: Brands
  tags:
  - Brands
  - Product readiness
- name: Marketplaces & channels
  tags:
  - Marketplace & channel
  - Channel
  - Events
  - Webhooks
  - Webhooks guide
- name: Fulfillment & delivery
  tags:
  - Fulfillment & delivery
- name: Reference
  tags:
  - Errors
  - Idempotency
  - Pagination
  - Rate limits
  - Versioning
  - Support
  - Changelog
  - Account
  - Portal
  - Brand portal
x-jenni-portal:
  injected_by: jenni-developer-portal/inject_guides.py
  sources:
  - pos-integration-brief.md
  - pos-catalog-inventory.md
  - brands.md
  - product-readiness.md
  - marketplace-channel.md
  - fulfillment-delivery.md
  - platform-basics.md
  - sandbox.md
  - retailers-and-claims.md
  - quickstart.md
  - authentication.md
  - scopes.md
  - errors.md
  - idempotency.md
  - pagination.md
  - rate-limits.md
  - versioning.md
  - webhooks.md
  - support.md
  - CHANGELOG.md
  - landing.md
  source_headings:
  - POS integration brief
  - 'Guide A — POS systems: catalog and inventory ingestion'
  - 'Guide D — Brands: product content through the ingestion lane'
  - Product readiness — what makes a product sellable
  - Marketplace and channel reads
  - Guide C — Fulfillment and delivery providers
  - Platform basics
  - Sandbox — what a self-serve key can do, what it cannot, and how to leave it
  - 'Guide E — Retailers: register a merchant and hand them a claim link'
  - Quickstart
  - Authentication
  - Scopes
  - Errors
  - Idempotency
  - Pagination
  - Rate limits
  - Versioning
  - Webhooks guide
  - Support
  - Changelog
  - JENNi Partner API
  landing: landing.md