Skip to main content

E-Commerce API

Manage your product catalog, track orders, monitor cart activity, and analyze customer RFM segments.

All endpoints on this page are relative to the base URL: https://api-eu1.joryio.com - see API Overview.

ID Naming Convention

Joryio uses a clear naming convention for identifiers:

Your IDs (use these when creating/tracking data)

FieldDescriptionExample
productIdYour product identifier (SKU, product ID from your e-commerce platform)"SKU-12345"
orderIdYour order identifier (order number from your platform)"ORD-2024-001"
userIdYour user identifier"user-123"

Joryio IDs (returned in API responses)

FieldDescriptionWhen to use
idJoryio's internal ID for products/ordersReturned in API responses, use for updates/deletes
joryioUserIdJoryio's internal user ID (the 24-hex id returned in user API responses)Alternative to userId when you want to reference by Joryio's ID

Flexible User Identification

When creating orders or tracking events, you can identify users using either:

  • userId - Your user identifier (most common)
  • joryioUserId - Joryio's internal user ID
// Using your user ID (recommended)
{ "orderId": "ORD-001", "userId": "user-123", ... }

// Using Joryio's internal user ID
{ "orderId": "ORD-001", "joryioUserId": "66a1f2c3d4e5f6a7b8c9d0e1", ... }

Authentication

All requests require JWT authentication:

Authorization: Bearer your_jwt_token
Content-Type: application/json
X-Workspace-Id: your_workspace_id

Product Catalog

Create Product

Create a new product in your catalog.

POST /catalog/products

Request Body:

FieldTypeRequiredDescription
productIdstringYesYour unique product identifier (SKU, product ID)
namestringYesProduct name
pricenumberYesProduct price
descriptionstringNoProduct description
compareAtPricenumberNoOriginal price (for discounts)
currencystringNoCurrency code (default: USD)
categoriesstring[]NoProduct categories
tagsstring[]NoProduct tags
brandstringNoBrand name
imageUrlstringNoMain product image URL
urlstringNoProduct page URL
inStockbooleanNoStock availability (default: true)
skustringNoStock keeping unit
variantsobject[]NoProduct variants
customFieldsobjectNoCustom attributes

Example Request:

curl -X POST https://api-eu1.joryio.com/catalog/products \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"productId": "SKU-12345",
"name": "Classic Blue T-Shirt",
"price": 29.99,
"compareAtPrice": 39.99,
"currency": "USD",
"categories": ["Clothing", "T-Shirts"],
"tags": ["sale", "bestseller"],
"brand": "Acme Apparel",
"imageUrl": "https://example.com/images/blue-tshirt.jpg",
"url": "https://example.com/products/blue-tshirt",
"inStock": true,
"sku": "BTS-001-BL",
"variants": [
{
"id": "var-s",
"name": "Small",
"sku": "BTS-001-BL-S",
"price": 29.99,
"inStock": true,
"options": { "size": "S", "color": "Blue" }
},
{
"id": "var-m",
"name": "Medium",
"sku": "BTS-001-BL-M",
"price": 29.99,
"inStock": true,
"options": { "size": "M", "color": "Blue" }
}
]
}'

Response:

{
"id": "prod_abc123",
"productId": "SKU-12345",
"name": "Classic Blue T-Shirt",
"price": 29.99,
"createdAt": "2024-01-20T10:00:00.000Z"
}

Bulk Upsert Products (array body)

There is no separate bulk endpoint: POST /catalog/products accepts either a single product object or a bare JSON array of product objects (no wrapper object). The array form creates or updates up to 500 products in one request, upserted by productId.

POST /catalog/products

Request Body:

A JSON array (max 500 elements; an empty array or more than 500 returns 400). Each element follows the same format as the single-object form, including the optional per-product source field.

Every element receives full validation. An invalid element is reported in failed by its array index - it is never silently accepted - and the remaining valid elements are still upserted.

[
{ "productId": "SKU-001", "name": "Product 1", "price": 19.99 },
{ "productId": "SKU-002", "name": "Product 2", "price": 29.99, "source": "shopify" }
]

Response:

Unlike the object form (which returns the created product), the array form returns an aggregate summary:

{
"processed": 2,
"upserted": 2,
"failed": []
}
FieldDescription
processedNumber of elements received in the request array
upsertedProducts actually created or updated
failedPer-element failures: index (position in the request array), productId / sku (when present on the element), reason

List Products

Query products with filtering and pagination.

GET /catalog/products

Query Parameters:

ParameterTypeDescription
searchstringSearch by name, description, or SKU
categoriesstring[]Filter by categories
tagsstring[]Filter by tags
brandstringFilter by brand
inStockbooleanFilter by stock status
minPricenumberMinimum price
maxPricenumberMaximum price
limitnumberResults per page (default: 50)
offsetnumberPagination offset
sortBystringSort field: name, price, createdAt, updatedAt
sortOrderstringasc or desc

Example:

curl "https://api-eu1.joryio.com/catalog/products?categories=T-Shirts&inStock=true&limit=20" \
-H "Authorization: Bearer $TOKEN"

Get Categories

List all product categories.

GET /catalog/categories

Get Brands

List all product brands.

GET /catalog/brands

Catalogue Stats

Totals and catalogue health, counted over every product - never over a page.

GET /catalog/stats

Response:

{
"productCount": 8214,
"categoryCount": 37,
"brandCount": 12,
"outOfStockCount": 341,
"lowStockCount": 96,
"unclassifiedCount": 18,
"lowStockThreshold": 10
}
FieldDescription
outOfStockCountProducts with quantity 0, or inStock: false when no quantity is tracked
lowStockCountProducts with quantity between 1 and lowStockThreshold
unclassifiedCountProducts missing a category or a brand
lowStockThresholdUnits-remaining boundary lowStockCount was computed at

Orders

Create Order

Track a new order.

POST /orders

Request Body:

FieldTypeRequiredDescription
orderIdstringYesYour order ID (order number from your platform)
userIdstringEither/OrYour customer user ID
joryioUserIdstringEither/OrJoryio's internal user ID (alternative to userId)
totalnumberYesOrder total
itemsobject[]YesLine items
statusstringNoOrder status (default: pending)
currencystringNoCurrency code
subtotalnumberNoSubtotal before discounts
discountnumberNoDiscount amount
shippingnumberNoShipping cost
taxnumberNoTax amount
totalRefundednumberNoAmount refunded so far, in the order currency (default 0). For a partial refund send the partial amount with status: partiallyRefunded; for a full refund set it to total with status: refunded. Refunds are netted out of attributed revenue.
couponCodestringNoApplied coupon
shippingAddressobjectNoShipping address
campaignIdstringNoAttribution campaign
canvasIdstringNoAttribution canvas
sourcestringNoOrder source (email, sms, direct)
utmSourcestringNoUTM source
utmMediumstringNoUTM medium
utmCampaignstringNoUTM campaign
User Identification

Either userId or joryioUserId must be provided. Use userId with your own user identifiers (most common). Use joryioUserId when you have Joryio's internal user ID (the 24-hex id returned in user API responses).

Line Item Structure:

{
"productId": "prod_abc123",
"name": "Blue T-Shirt",
"sku": "BTS-001",
"quantity": 2,
"price": 29.99,
"total": 59.98,
"imageUrl": "https://example.com/image.jpg"
}

Example Request:

curl -X POST https://api-eu1.joryio.com/orders \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"orderId": "ORD-2024-001",
"userId": "user-123",
"total": 89.97,
"subtotal": 99.97,
"discount": 10.00,
"shipping": 5.99,
"tax": 4.99,
"currency": "USD",
"couponCode": "SAVE10",
"items": [
{
"productId": "prod_abc123",
"name": "Blue T-Shirt",
"quantity": 2,
"price": 29.99,
"total": 59.98
},
{
"productId": "prod_def456",
"name": "Black Jeans",
"quantity": 1,
"price": 49.99,
"total": 49.99
}
],
"source": "email",
"campaignId": "camp_xyz789"
}'

Fulfill Order

Mark an order as shipped.

POST /orders/:id/fulfill

Request Body:

{
"trackingNumber": "1Z999AA10123456784",
"carrier": "UPS"
}

Cancel Order

Cancel an order.

POST /orders/:id/cancel

Request Body:

{
"reason": "Customer requested cancellation"
}

Refund Order

Process a refund.

POST /orders/:id/refund

Request Body:

{
"refundAmount": 29.99,
"reason": "Product defective",
"partial": true
}

Order Statistics

Get order statistics for a date range.

GET /orders/stats?startDate=2024-01-01&endDate=2024-01-31

Response:

{
"totalOrders": 156,
"totalRevenue": 12450.50,
"averageOrderValue": 79.81,
"ordersByStatus": {
"pending": 5,
"processing": 12,
"shipped": 45,
"delivered": 90,
"cancelled": 4
}
}

Cart Tracking

Update Cart

Track or update a user's cart.

PUT /carts

Request Body:

{
"userId": "user_123",
"items": [
{
"productId": "prod_abc123",
"name": "Blue T-Shirt",
"price": 29.99,
"quantity": 2,
"total": 59.98,
"imageUrl": "https://example.com/image.jpg"
}
],
"value": 59.98,
"currency": "USD",
"checkoutUrl": "https://store.example.com/checkout?cart=abc123"
}

Add to Cart

Add an item to a user's cart.

POST /carts/add

Request Body:

{
"userId": "user_123",
"item": {
"productId": "prod_abc123",
"name": "Blue T-Shirt",
"price": 29.99,
"quantity": 1,
"total": 29.99
}
}

Get Abandoned Carts

List abandoned carts for recovery campaigns.

GET /carts/abandoned

Query Parameters:

ParameterTypeDescription
minValuenumberMinimum cart value
abandonedMinutesAgonumberMax minutes since abandonment
limitnumberResults per page
offsetnumberPagination offset

Response:

{
"data": [
{
"id": "cart_123",
"userId": "user_456",
"items": [...],
"value": 149.99,
"abandonedAt": "2024-01-20T15:30:00.000Z",
"checkoutUrl": "https://store.example.com/checkout?cart=abc"
}
],
"total": 42,
"hasMore": true
}

Cart Statistics

Get cart and abandonment statistics.

GET /carts/stats

Response:

{
"totalCarts": 1250,
"abandonedCarts": 312,
"recoveredCarts": 87,
"totalAbandonedValue": 45670.50,
"recoveryRate": 27.88
}

RFM Analysis

RFM (Recency, Frequency, Monetary) analysis segments customers based on purchase behavior.

Get User RFM Data

Get RFM scores and segment for a specific user.

GET /rfm/user/:userId

Response:

{
"totalOrders": 12,
"totalSpent": 849.50,
"averageOrderValue": 70.79,
"firstOrderDate": "2023-06-15T10:00:00.000Z",
"lastOrderDate": "2024-01-18T14:30:00.000Z",
"daysSinceLastOrder": 2,
"hasActiveCart": false,
"rfmRecency": 5,
"rfmFrequency": 4,
"rfmMonetary": 4,
"rfmScore": "544",
"rfmSegment": "champions"
}

Get RFM Distribution

Get customer distribution across RFM segments.

GET /rfm/distribution

Response:

{
"champions": { "count": 150, "totalValue": 125000 },
"loyal": { "count": 320, "totalValue": 89000 },
"potential_loyalists": { "count": 180, "totalValue": 32000 },
"new_customers": { "count": 450, "totalValue": 28000 },
"at_risk": { "count": 95, "totalValue": 42000 },
"lost": { "count": 280, "totalValue": 15000 }
}

RFM Segments

SegmentDescriptionTypical RFM Scores
ChampionsBest customers, buy often, spend most555, 554, 545
LoyalConsistent customers444, 443, 434
Potential LoyalistsRecent with average frequency433, 343, 333
New CustomersJust made first purchase511, 512, 411
PromisingRecent but low frequency422, 322, 312
Needs AttentionAverage, declining engagement332, 322, 233
About to SleepBelow average, at risk211, 212, 221
At RiskWere loyal, haven't bought recently144, 143, 244
Can't LoseWere best customers, now inactive155, 154, 255
HibernatingLow engagement, long inactive122, 121, 112
LostLowest scores, likely churned111

Webhooks

E-commerce events emit webhooks that can trigger canvas flows:

EventDescription
ecommerce.order.createdNew order placed
ecommerce.order.fulfilledOrder shipped
ecommerce.order.cancelledOrder cancelled
ecommerce.order.refundedOrder refunded
ecommerce.order.status_changedOrder status changed
ecommerce.cart.abandonedCart marked as abandoned
ecommerce.cart.recoveredAbandoned cart recovered
ecommerce.cart.updatedCart contents changed

Lookup by Your IDs

Get Product by Your Product ID

Retrieve a product using your product identifier.

GET /catalog/products/by-product-id/:productId

Example:

curl "https://api-eu1.joryio.com/catalog/products/by-product-id/SKU-12345" \
-H "Authorization: Bearer $TOKEN"

Get Order by Your Order ID

Retrieve an order using your order identifier.

GET /orders/by-order-id/:orderId

Example:

curl "https://api-eu1.joryio.com/orders/by-order-id/ORD-2024-001" \
-H "Authorization: Bearer $TOKEN"

Error Responses

{
"statusCode": 404,
"message": "Product SKU-12345 not found",
"error": "Not Found"
}
Status CodeDescription
400Invalid request body
401Authentication required
403Insufficient permissions
404Resource not found
409Conflict (duplicate external ID)
500Internal server error