Conventions that apply to every request and every response, regardless of which endpoint you are calling.
Conventions that apply to every request and every response, regardless of which endpoint you are calling.
Base URL and versioning
https://api.jooraccess.com
Every catalog and webhook endpoint on this page lives under /v4. The version is in the path, so a future /v5 cannot break your integration. The token endpoint is the one exception: it is not versioned and sits at /oauth2/token/.
Additive changes happen inside a version. New fields can appear in responses and new values can appear in enumerated fields such as the webhook event. Ignore keys you do not recognise rather than failing on them. Fields are never removed or retyped inside a version.
One path is worth pointing out because it is the exception: /v4/connections has no retailers segment. The endpoint serves both sides of a connection, so a brand caller gets its connected retailers and a retailer caller gets its connected brands. Every other endpoint here is under /v4/retailers/.
The response envelope
Every response uses the same envelope. There are two shapes.
Single resource
{
"data": { "id": 12345, "name": "Versilia 105" },
"errors": []
}Collection
{
"data": [ { "id": 12345 }, { "id": 12346 } ],
"errors": [],
"pagination": {
"limit": 100,
"next_cursor": "WzQyLDEyMzQ1XQ==",
"next_page": "/v4/retailers/collections/1146216/products?account=40975&limit=100&cursor=WzQyLDEyMzQ1XQ=="
}
}Three things to hold onto:
errorsis always present, and is an empty array on success. Check it, do not assume the status code tells the whole story.- On an error,
datais an empty array, notnull. next_pageis a path with a query string, not an absolute URL. Join it onto the base URL yourself. It preserves every query parameter you sent, includingaccount, and replaceslimitandcursor.
Dates and times
| Kind | Format | Example |
|---|---|---|
| Timestamps | ISO 8601, UTC, second precision | "2026-08-08T12:34:56Z" |
| Calendar dates | Plain date, no time | "2027-01-15" |
Delivery windows and cutoff dates are calendar dates. updated_at, created, modified and webhook timestamps are timestamps.
Calendar dates carry no timezone and this page does not say which one to read them in, which matters for a cutoff date. Ask your JOOR contact to confirm before you build cutoff logic on it.
Identifiers and what you may key on
| Identifier | Notes | See also |
|---|---|---|
Image id | Integer, and not stable across an edit. An edit creates a new image with a new id and a new url, and the old id leaves the array | Products & Prices, "Image URLs" |
cursor and next_cursor | Opaque string. Echo back unchanged, never decode, never persist as a long lived bookmark | "Pagination" below |
Whether product.id, color.id and size_id survive a product leaving your catalog and coming back is not stated. That decides whether a returning product upserts onto your old row or arrives as a new one. Ask your JOOR contact before you key your database on it.
Pagination
Every list endpoint is keyset paginated. There is no page or offset parameter anywhere.
# first page: no cursor
curl -G .../products -d account=40975 -d limit=100
# next page: echo the cursor back, unchanged
curl -G .../products -d account=40975 -d limit=100 -d cursor='WzQyLDEyMzQ1XQ=='- Treat
next_cursoras opaque. Echo it back ascursorand nothing else. Do not decode it, derive from it, or store it as a long lived bookmark. Its shape belongs to the sort order of the endpoint and can change. next_cursorisnullon the last page.- Pages are stable while the catalog changes underneath you. Rows inserted or removed behind your cursor cannot make a row duplicate or vanish on a later page.
- Deep pages cost the same as shallow ones, so a long walk does not slow down as it goes.
A full seed of an enterprise sized catalog spans several rate limit windows and can span a token refresh, so a walk will be interrupted. How long a cursor stays usable, and whether a resumed walk is safe or must start again, is not stated here. Ask your JOOR contact before you design retry logic around a stored cursor.
Page size limits
| Endpoint | Default | Maximum |
|---|---|---|
GET /v4/connections | 100 | 100 |
GET /v4/retailers/brands/{brand_id}/collections | 100 | 100 |
GET /v4/retailers/collections/{id}/products | 100 | 100 |
GET /v4/retailers/collections/{id}/products?detail=full | 25 | 25 |
GET /v4/retailers/subscriptions | 20 | 20 |
GET /v4/retailers/subscriptions/{id}/deliveries | 20 | 20 |
Asking for more than the maximum returns 400. The limits differ by endpoint because a detail=full page carries every color, SKU and price of 25 products, which is a far larger body than 100 summary rows.
