APIs / Store
An online shop: products, customers, orders, reviews and stock.
Two thousand products in eight categories, eight hundred customers, three thousand orders with their line items, reviews, three warehouses of stock and open carts. Orders move through a lifecycle (pending → paid → shipped → delivered) and the live stream shows orders moving. 20,730 records in all.
In Sonda: Import → From a URL with the OpenAPI address and the whole API lands as a project, one request per operation with example bodies. No keys, no headers to add. More on each protocol.
_note and X-Sondahub-Write: simulated. A GET afterwards will not find what you wrote.curl https://api.sondahub.com/v1/store
Every list answers { "data": [...], "meta": { "page", "limit", "total", "pages" } } with X-Total-Count and Link headers (next, prev, first, last). These options work on every collection and every nested route:
| Option | Meaning | Example |
|---|---|---|
page, limit | Paging, 1-based; limit 1–200, default 20. offset works too. | ?page=3&limit=50 |
sort | Comma list of fields, - for descending. Default id here. | ?sort=-price,id |
field=value | Equals. Booleans as true/false, null for missing. | ?status=active |
_ne _gt _gte _lt _lte | Not equal and comparisons, on numbers, dates and strings. | ?price_gte=10&price_lt=100 |
_like | Contains, case-insensitive. | ?sku_like=an |
_in | Any of a comma list. | ?id_in=1,2,3 |
_null | true: missing; false: present. | ?slug_null=true |
a.b=value | Inside a JSON field, dotted. | ?address.line1=… |
q | Search across the text fields. | ?q=alpine |
fields | Only these fields back. | ?fields=id,sku |
expand | Embed related records. | ?expand=category,reviews |
A name that is not a field answers 400 and lists the fields. Writes answer 422 with one line per problem, 404 for a missing id, 405 with an Allow header for a verb a route does not take.
The eight product categories. 8 records — the file.
| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
namerequired | string | |
slugrequired | string | |
description | text | |
product_countread-only | int | Seed products in the category. |
Relations: products → the products whose category_id is this category. Use ?expand=products to embed them, or the routes below.
curl "https://api.sondahub.com/v1/store/categories?limit=3"
curl https://api.sondahub.com/v1/store/categories/1
curl "https://api.sondahub.com/v1/store/categories/1/products?limit=5"
curl -X POST https://api.sondahub.com/v1/store/categories \
-H "Content-Type: application/json" \
-d '{"name":"Audio","slug":"audio"}'
curl -X PATCH https://api.sondahub.com/v1/store/categories/1 \
-H "Content-Type: application/json" \
-d '{"name":"Changed name"}'
curl -X DELETE https://api.sondahub.com/v1/store/categories/1
What the store sells. Prices are in USD. 2,000 records — the file.
| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
skurequired | string | Unique stock keeping unit. |
namerequired | string | |
slug | string | |
description | text | |
category_idrequired | int → categories | |
brand | string | |
pricerequired | float | min 0 |
compare_at_price | float | The struck-through price on sale, or null. |
currency | string | |
color | string | |
weight_g | int | min 0 |
tags | json string[] | |
in_stock | bool | |
ratingread-only | float | Average of the seed reviews, 1–5. |
review_countread-only | int | |
status | enum | active draft archived |
Relations: category → one category through category_id; reviews → the reviews whose product_id is this product; inventory → the inventory whose product_id is this product. Use ?expand=category,reviews,inventory to embed them, or the routes below.
curl "https://api.sondahub.com/v1/store/products?status=draft&price_gte=10&expand=category&limit=3"
curl https://api.sondahub.com/v1/store/products/1?expand=category
curl "https://api.sondahub.com/v1/store/products/1/reviews?limit=5"
curl -X POST https://api.sondahub.com/v1/store/products \
-H "Content-Type: application/json" \
-d '{"sku":"AUD-4F7K2M","name":"Sonora Wireless Headphones","slug":"sonora-wireless-headphones","category_id":1,"brand":"Sonora","price":149.99,"currency":"USD","color":"Graphite","tags":["bluetooth","travel"],"status":"active"}'
curl -X PATCH https://api.sondahub.com/v1/store/products/1 \
-H "Content-Type: application/json" \
-d '{"status":"draft"}'
curl -X DELETE https://api.sondahub.com/v1/store/products/1
People who buy. Addresses are nested objects; filter on them with a dotted name (?address.country=AR). 800 records — the file.
| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
namerequired | string | |
emailrequired | string | |
phone | string | |
company | string | |
address | json { line1, line2?, city, region, postal_code, country } | |
tier | enum | standard silver gold platinum |
marketing_opt_in | bool | |
orders_countread-only | int | |
total_spentread-only | float | |
notes | text |
Relations: orders → the orders whose customer_id is this customer; reviews → the reviews whose customer_id is this customer. Use ?expand=orders,reviews to embed them, or the routes below.
curl "https://api.sondahub.com/v1/store/customers?tier=silver&limit=3"
curl https://api.sondahub.com/v1/store/customers/1
curl "https://api.sondahub.com/v1/store/customers/1/orders?limit=5"
curl -X POST https://api.sondahub.com/v1/store/customers \
-H "Content-Type: application/json" \
-d '{"name":"Camila Fernandez","email":"[email protected]","tier":"standard"}'
curl -X PATCH https://api.sondahub.com/v1/store/customers/1 \
-H "Content-Type: application/json" \
-d '{"tier":"silver"}'
curl -X DELETE https://api.sondahub.com/v1/store/customers/1
An order and its money. Line items live in order_items (also at /orders/{id}/items and ?expand=items). 3,000 records — the file.
items: [{ product_id, quantity }]: the hub prices them from the products, computes subtotal, shipping, tax and total, creates the order items and returns them inline. PATCH to shipped stamps shipped_at and a tracking number; delivered stamps delivered_at.| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
numberread-only | string | Human order number. |
customer_idrequired | int → customers | |
status | enum | pending paid shipped delivered cancelled refunded |
subtotal | float | min 0 |
shipping | float | min 0 |
tax | float | min 0 |
discount | float | min 0 |
total | float | min 0 |
currency | string | |
payment_method | enum | card paypal bank_transfer cash_on_delivery |
shipping_address | json { line1, line2?, city, region, postal_code, country } | |
shipping_method | enum | standard express overnight pickup |
tracking_number | string | |
placed_at | datetime | Defaults to now on POST. |
paid_at | datetime | |
shipped_at | datetime | |
delivered_at | datetime | |
notes | text |
Relations: customer → one customer through customer_id; items → the order items whose order_id is this order. Use ?expand=customer,items to embed them, or the routes below.
curl "https://api.sondahub.com/v1/store/orders?status=paid&subtotal_gte=10&expand=customer&limit=3"
curl https://api.sondahub.com/v1/store/orders/1?expand=customer
curl "https://api.sondahub.com/v1/store/orders/1/items?limit=5"
curl -X POST https://api.sondahub.com/v1/store/orders \
-H "Content-Type: application/json" \
-d '{"customer_id":1,"status":"paid","currency":"USD","payment_method":"card","shipping_method":"standard","items":[{"product_id":1,"quantity":2}]}'
curl -X PATCH https://api.sondahub.com/v1/store/orders/1 \
-H "Content-Type: application/json" \
-d '{"status":"paid"}'
curl -X DELETE https://api.sondahub.com/v1/store/orders/1
One product line on an order. 6,219 records — the file.
| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
order_idrequired | int → orders | |
product_idrequired | int → products | |
sku | string | |
name | string | The product name at the time of the order. |
quantityrequired | int | min 1, max 999 |
unit_price | float | min 0 |
line_total | float | min 0 |
Relations: order → one order through order_id; product → one product through product_id. Use ?expand=order,product to embed them, or the routes below.
curl "https://api.sondahub.com/v1/store/order_items?quantity_gte=1&expand=order&limit=3"
curl https://api.sondahub.com/v1/store/order_items/1?expand=order
curl -X POST https://api.sondahub.com/v1/store/order_items \
-H "Content-Type: application/json" \
-d '{"order_id":1,"product_id":1,"quantity":1}'
curl -X PATCH https://api.sondahub.com/v1/store/order_items/1 \
-H "Content-Type: application/json" \
-d '{"quantity":2}'
curl -X DELETE https://api.sondahub.com/v1/store/order_items/1
Customer reviews, 1–5 stars. 2,500 records — the file.
rating and review_count — in the answer’s world, which ends with the answer.| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
product_idrequired | int → products | |
customer_idrequired | int → customers | |
ratingrequired | int | min 1, max 5 |
title | string | |
body | text | |
verified_purchase | bool | |
helpful_votes | int | min 0 |
Relations: product → one product through product_id; customer → one customer through customer_id. Use ?expand=product,customer to embed them, or the routes below.
curl "https://api.sondahub.com/v1/store/reviews?rating_gte=1&expand=product&limit=3"
curl https://api.sondahub.com/v1/store/reviews/1?expand=product
curl -X POST https://api.sondahub.com/v1/store/reviews \
-H "Content-Type: application/json" \
-d '{"product_id":1,"customer_id":1,"rating":1}'
curl -X PATCH https://api.sondahub.com/v1/store/reviews/1 \
-H "Content-Type: application/json" \
-d '{"rating":2}'
curl -X DELETE https://api.sondahub.com/v1/store/reviews/1
Where stock sits. 3 records — the file.
| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
coderequired | string | |
namerequired | string | |
address | json { line1, city, region, postal_code, country } | |
timezone | string |
Relations: inventory → the inventory whose warehouse_id is this warehouse. Use ?expand=inventory to embed them, or the routes below.
curl "https://api.sondahub.com/v1/store/warehouses?limit=3"
curl https://api.sondahub.com/v1/store/warehouses/1
curl "https://api.sondahub.com/v1/store/warehouses/1/inventory?limit=5"
curl -X POST https://api.sondahub.com/v1/store/warehouses \
-H "Content-Type: application/json" \
-d '{"code":"MIA","name":"A name"}'
curl -X PATCH https://api.sondahub.com/v1/store/warehouses/1 \
-H "Content-Type: application/json" \
-d '{"code":"Changed code"}'
curl -X DELETE https://api.sondahub.com/v1/store/warehouses/1
Stock of a product at a warehouse. 6,000 records — the file.
| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
product_idrequired | int → products | |
warehouse_idrequired | int → warehouses | |
on_handrequired | int | min 0 |
reserved | int | min 0 |
reorder_point | int | min 0 |
bin | string | |
counted_at | datetime |
Relations: product → one product through product_id; warehouse → one warehouse through warehouse_id. Use ?expand=product,warehouse to embed them, or the routes below.
curl "https://api.sondahub.com/v1/store/inventory?on_hand_gte=1&expand=product&limit=3"
curl https://api.sondahub.com/v1/store/inventory/1?expand=product
curl -X POST https://api.sondahub.com/v1/store/inventory \
-H "Content-Type: application/json" \
-d '{"product_id":1,"warehouse_id":1,"on_hand":1,"bin":"A-14-3"}'
curl -X PATCH https://api.sondahub.com/v1/store/inventory/1 \
-H "Content-Type: application/json" \
-d '{"on_hand":2}'
curl -X DELETE https://api.sondahub.com/v1/store/inventory/1
Open shopping carts; items are a nested array, so a cart is one document. 200 records — the file.
| Field | Type | Notes |
|---|---|---|
idread-only | int | Assigned by the server. Seed records keep their ids across restarts; records you create continue after the seed. |
created_atread-only | datetime | When the record was created (ISO 8601, UTC). |
updated_atread-only | datetime | When the record last changed. |
customer_id | int → customers | |
session_id | string | For anonymous carts. |
status | enum | open abandoned converted |
items | json { product_id, sku, name, quantity, unit_price }[] | |
item_count | int | min 0 |
subtotal | float | min 0 |
coupon | string | |
last_activity_at | datetime |
Relations: customer → one customer through customer_id. Use ?expand=customer to embed them, or the routes below.
curl "https://api.sondahub.com/v1/store/carts?status=abandoned&item_count_gte=1&expand=customer&limit=3"
curl https://api.sondahub.com/v1/store/carts/1?expand=customer
curl -X POST https://api.sondahub.com/v1/store/carts \
-H "Content-Type: application/json" \
-d '{"status":"open"}'
curl -X PATCH https://api.sondahub.com/v1/store/carts/1 \
-H "Content-Type: application/json" \
-d '{"status":"abandoned"}'
curl -X DELETE https://api.sondahub.com/v1/store/carts/1
The same stream two ways: the world's own activity, one tick a second, generated for your connection alone. Both push JSON text messages; SSE names each one with event: and numbers it with id:. ?topics=a,b narrows either.
| Topic | What arrives | How often |
|---|---|---|
orders | An order changing status (paid → shipped → delivered). | 4 s |
inventory | A stock level moving at a warehouse. | 6 s |
wss://api.sondahub.com/v1/store/ws?topics=orders
> {"type":"hello","api":"store","topics":[…],"subscribed":[…]}
> {"type":"event","topic":"orders","api":"store","ts":"…","data":{…}}
< {"type":"subscribe","topics":["orders"]} # narrow to some topics
< {"type":"ping"} # → {"type":"pong"}
< anything else # → echoed back as {"type":"echo"}
curl -N "https://api.sondahub.com/v1/store/events?topics=orders"
retry: 3000
id: 1
event: orders
data: {"type":"event","topic":"orders",…}
One endpoint, https://api.sondahub.com/v1/store/graphql: POST {"query", "variables"} or GET ?query=. Introspection is on, so Sonda's GraphQL mode loads the schema; the SDL is a click away. Every collection is a paged query with the same filter, sort and q options as REST (operators as suffixes: price_lt), a by-id query, relation fields both ways, and create, update, replace and delete mutations — simulated like every write, with the note in extensions.
curl https://api.sondahub.com/v1/store/graphql -H "Content-Type: application/json" -d '{"query": "{ products(limit: 3, sort: \"-id\", filter: { status: active }) { total data { id sku name slug category { name } reviews(limit: 2) { id } } } }"}'
{
products(limit: 3, sort: "-id", filter: { status: active }) {
total
data {
id sku name slug
category { name }
reviews(limit: 2) { id }
}
}
}