Skip to content

SLO API

Build against the canonical SLO REST API, then use the agent endpoint when tool-call semantics are a better fit. Both surfaces share the same underlying application logic.

What can you do with it?

The REST API under /api/* is the long-term contract for shop automation and integrations. The agent endpoint is an adapter for tool-based clients, not a separate business-logic surface.

Products

Create, update, and remove products. Set prices, descriptions, and inventory — all at once or one at a time.

Orders

See incoming orders, confirm or cancel them, update fulfillment status as you pack and deliver.

Inventory

Update stock levels globally or per channel. Keep availability in sync without manual entry.

Channels

List your sales channels — delivery, pickup, wholesale — and manage which ones are active.

Start with REST when you want stable resource-oriented contracts. Use the agent endpoint when you want the same capabilities exposed as tool calls.

Quick start

1

Get an API key

Shop admin → Apps → Create Key. Copy it — shown once.

2

Call the canonical REST API

curl -X GET 'https://slo.earth/api/channels?shopId=shop_123' \
  -H "Authorization: Bearer sk_live_..."
3

Use the agent adapter when needed

curl -X POST https://slo.earth/api/agent \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"tool":"listProducts"}'
4

Get structured data back

{
  "ok": true,
  "result": {
    "products": [...],
    "total": 12
  }
}

Endpoints

Canonical REST API
GET/POST https://slo.earth/api/*

Agent adapter
POST https://slo.earth/api/agent

Authorization: Bearer sk_live_...

REST example

GET /api/channels?shopId=shop_123

POST /api/channels
{
  "action": "create",
  "shopId": "shop_123",
  "title": "Wholesale"
}

Agent example

POST /api/agent
{
  "tool": "createChannel",
  "params": { "title": "Wholesale" }
}

Mutation response

{
  "success": true,
  "requestId": "req_123",
  "replayed": false
}

Error

{
  "error": "Forbidden",
  "requestId": "req_123"
}

Authentication

Use a bearer token in the Authorization header. Each key is scoped to one shop and can be revoked at any time. For mutating requests, you can also send Idempotency-Key and X-Request-Id.

Authorization: Bearer sk_live_your_key_here
Idempotency-Key: optional-but-recommended-for-mutations
X-Request-Id: optional-client-request-id

Tools

products

listProducts

List products for your shop. Use the 'search' param to filter by name/variety when looking for specific products — avoids returning the full catalog.

Params

limitnumberMax products to return
offsetnumberNumber of products to skip
searchstringFilter by title or variety (case-insensitive)
{
  "tool": "listProducts",
  "params": {}
}

{ products: Product[], total: number }

getProduct

Get a single product by ID

Params

productIdstringrequiredThe product _id
{
  "tool": "getProduct",
  "params": {
    "productId": "product_abc123"
  }
}

{ product: Product }

createProduct

Create a new product

Params

titlestringrequiredProduct title
descriptionstringProduct description
pricenumberBase price per kg/L/piece in SEK (NOT per selling unit — the system calculates per-unit price)
inventoryTypestringHow inventory is tracked: "weight" (bulk kg), "volume" (bulk L), or "piece" (counted items). Default: weight
unitTypestringWhat the customer buys: "weight", "volume", or "item". For bulk: matches inventoryType. For pre-packaged: "piece" inventory + "weight"/"volume" unitType
unitnumberSelling unit size in g/mL. Default 1000 (=1kg or 1L). Examples: 10000 for 10kg batch, 500 for 500g bag
unitStepnumberMinimum order increment. For bulk weight/volume: 0.1 = customer can order in 100g/100mL steps. For piece: 1 = one at a time
varietystringProduct variety/cultivar (e.g. "King Edward", "Roma")
hideFromStorefrontbooleanHide from public storefront
taxnumberTax rate as decimal (e.g. 0.12 for 12%)
hideFromCalendarbooleanHide from harvest calendar
transportCategorystringTemperature zone for delivery: "frozen", "refrigerated", or "roomTemperature" (default)
{
  "tool": "createProduct",
  "params": {
    "title": "Organic Tomatoes"
  }
}

{ product: Product }

updateProduct

Update an existing product. Only send fields you want to change.

Params

productIdstringrequiredThe product _id
titlestringProduct title
varietystringProduct variety/cultivar (e.g. "King Edward", "Roma")
descriptionstringProduct description
pricenumberBase price per kg/L/piece in SEK (NOT per selling unit — the system calculates per-unit price)
inventoryTypestringHow inventory is tracked: "weight" (bulk kg), "volume" (bulk L), or "piece" (counted items)
unitTypestringWhat the customer buys: "weight", "volume", or "item". For bulk: matches inventoryType. For pre-packaged: "piece" inventory + "weight"/"volume" unitType
unitnumberSelling unit size in g/mL. Examples: 10000 for 10kg batch, 500 for 500g bag, 1000 for 1kg/1L
unitStepnumberMinimum order increment. For bulk weight/volume: 0.1 = 100g/100mL steps. For piece: 1 = one at a time
hideFromStorefrontbooleanHide from public storefront
taxnumberTax rate as decimal (e.g. 0.12 for 12%)
hideFromCalendarbooleanHide from harvest calendar
channelPricesstringJSON array of channel price overrides, e.g. [{"channelId":"abc","price":50}]
channelInventorystringJSON array of channel inventory overrides, e.g. [{"channelId":"abc","available":10}]
channelUnitsstringJSON array of channel unit overrides, e.g. [{"channelId":"abc","unit":500,"unitStep":0.1}]
harvestStartstringHarvest start date (YYYY-MM-DD)
harvestEndstringHarvest end date (YYYY-MM-DD)
transportCategorystringTemperature zone for delivery: "frozen", "refrigerated", or "roomTemperature" (default)
{
  "tool": "updateProduct",
  "params": {
    "productId": "product_abc123"
  }
}

{ product: Product }

batchUpdateProducts

Update multiple products at once. Send a JSON array of updates as a string.

Params

updatesstringrequiredJSON array of objects, each with productId and fields to update. Example: [{"productId":"abc","price":200},{"productId":"def","title":"New name"}]
{
  "tool": "batchUpdateProducts",
  "params": {
    "updates": "..."
  }
}

{ results: { productId, ok, title?, error? }[] }

deleteProduct

Delete a product

Params

productIdstringrequiredThe product _id
{
  "tool": "deleteProduct",
  "params": {
    "productId": "product_abc123"
  }
}

{ deleted: true }

uploadProductImage

Set a product's primary image. IMPORTANT: When the user has attached an image in chat, ALWAYS use assetId from the attachment metadata — NEVER use imageBase64. The assetId is shown in the attachment info like 'Sanity asset ID: image-xxx'. Only use imageUrl for external URLs. Never generate or fabricate base64 data.

Params

productIdstringrequiredThe product _id
assetIdstringSanity asset ID (e.g. 'image-abc123-1920x1080-jpg') from a user-uploaded attachment. Preferred when available.
imageUrlstringPublic HTTPS URL of the image
imageBase64stringBase64-encoded image data (e.g. from a user-uploaded file)
mimeTypestringMIME type when using imageBase64 (e.g. "image/png", "image/jpeg"). Defaults to image/png.
{
  "tool": "uploadProductImage",
  "params": {
    "productId": "product_abc123"
  }
}

{ success: boolean, assetRef: string, productId: string }

inventory

updateInventory

Update inventory for a product (global or per-channel)

Params

productIdstringrequiredThe product _id
availablenumberAvailable stock in display units: kg for weight, L for volume, count for piece (e.g. pass 2 for 2 kg, not 2000)
channelIdstringOptional: update channel-specific inventory instead of global
{
  "tool": "updateInventory",
  "params": {
    "productId": "product_abc123"
  }
}

{ product: Product }

batchUpdateInventory

Update inventory for multiple products in one call. More efficient than calling updateInventory multiple times.

Params

updatesstringrequiredJSON array: [{"productId":"...","available":10},{"productId":"...","available":25}]. Values in display units (kg/L/count).
{
  "tool": "batchUpdateInventory",
  "params": {
    "updates": "..."
  }
}

{ results: { productId, title, variety, previousAvailable, newAvailable, unit, imageUrl }[] }

orders

listOrders

List orders for your shop

Params

dropIdstringFilter by drop _id
statusstringFilter by status
limitnumberMax orders to return (default: 50)
offsetnumberNumber of orders to skip
{
  "tool": "listOrders",
  "params": {}
}

{ orders: Order[], total: number }

getOrder

Get a single order by _id or order number

Params

orderIdstringrequiredThe order _id or order number (e.g. '97')
{
  "tool": "getOrder",
  "params": {
    "orderId": "order_abc123"
  }
}

{ order: Order }

updateOrderStatus

Update the status of an order

Params

orderIdstringrequiredThe order _id or order number (e.g. '97')
statusstringrequiredNew status (e.g. "confirmed", "delivered", "cancelled")
{
  "tool": "updateOrderStatus",
  "params": {
    "orderId": "order_abc123",
    "status": "active"
  }
}

{ order: Order }

confirmOrder

Confirm a pending order. Sets correct status based on payment type (invoice → confirmedInvoice, swish → paid), sets fulfillment to packing, and notifies the buyer.

Params

orderIdstringrequiredThe order _id or order number (e.g. '97')
notifyBuyerbooleanSet to false to skip buyer notifications for this action.
{
  "tool": "confirmOrder",
  "params": {
    "orderId": "order_abc123"
  }
}

{ order: Order }

cancelOrder

Cancel an order. Refunds inventory and notifies the buyer.

Params

orderIdstringrequiredThe order _id or order number (e.g. '97')
cancelledBystringWho initiated the cancellation: "buyer" or "seller". Defaults to "seller".
notifyBuyerbooleanSet to false to skip buyer notifications for this action.
{
  "tool": "cancelOrder",
  "params": {
    "orderId": "order_abc123"
  }
}

{ success: true }

updateFulfillment

Update order fulfillment status. Valid statuses: packing, ready, inTransit, delivered. Notifies the buyer on status change.

Params

orderIdstringrequiredThe order _id or order number (e.g. '97')
statusstringrequiredNew fulfillment status: packing, ready, inTransit, or delivered
notifyBuyerbooleanSet to false to skip buyer notifications for this action.
{
  "tool": "updateFulfillment",
  "params": {
    "orderId": "order_abc123",
    "status": "active"
  }
}

{ order: Order }

editOrderItems

Edit item prices/quantities and/or remove items from an existing order. Quantities are in display units. Inventory is auto-adjusted for pending orders.

Params

orderIdstringrequiredThe order _id or order number (e.g. '97')
editsstringJSON array: [{"itemKey":"...","price":75,"quantity":2}]. Include only fields to change.
removalsstringJSON array of item _key strings to remove: ["key1","key2"]
notifyBuyerbooleanSet to false to skip buyer notifications for this action.
{
  "tool": "editOrderItems",
  "params": {
    "orderId": "order_abc123"
  }
}

{ order: Order }

addOrderItems

Add one or more products to an existing order. Quantities in display units (kg/L/count). Inventory is auto-adjusted.

Params

orderIdstringrequiredThe order _id or order number (e.g. '97')
itemsstringrequiredJSON array: [{"productId":"...","quantity":2}]. Quantity in display units.
notifyBuyerbooleanSet to false to skip buyer notifications for this action.
{
  "tool": "addOrderItems",
  "params": {
    "orderId": "order_abc123",
    "items": "..."
  }
}

{ order: Order }

addOrderItemsFromCart

Append one or more pre-built order items to an existing order. Use this when the items were already constructed by the cart builder.

Params

orderIdstringrequiredThe order _id or order number (e.g. '97')
itemsarrayrequiredArray of pre-built OrderItem objects to append.
notifyBuyerbooleanSet to false to skip buyer notifications for this action.
{
  "tool": "addOrderItemsFromCart",
  "params": {
    "orderId": "order_abc123",
    "items": []
  }
}

{ order: Order }

updateDeliveryPrice

Update the delivery/pickup price on an existing order. Pass the new ex-VAT price. Tax summary auto-recalculates.

Params

orderIdstringrequiredThe order _id or order number (e.g. '97')
deliveryPricenumberrequiredNew delivery price (ex-VAT). Use 0 for free delivery.
notifyBuyerbooleanSet to false to skip buyer notifications for this action.
{
  "tool": "updateDeliveryPrice",
  "params": {
    "orderId": "order_abc123",
    "deliveryPrice": 10
  }
}

{ order: Order }

capturePayment

Capture a Stripe payment for an uncaptured order, mark it paid, and move fulfillment to packing.

Params

orderIdstringrequiredThe order _id or order number (e.g. '97')
notifyBuyerbooleanSet to false to skip buyer notifications for this action.
{
  "tool": "capturePayment",
  "params": {
    "orderId": "order_abc123"
  }
}

{ order: Order }

cancelPayment

Cancel a Stripe payment and mark the order as cancelled.

Params

orderIdstringrequiredThe order _id or order number (e.g. '97')
{
  "tool": "cancelPayment",
  "params": {
    "orderId": "order_abc123"
  }
}

{ success: true }

createOrder

Create an order. Known buyers (entityId) get a finalized order immediately (inventory overdraft is allowed — the seller already confirmed via estimate). Unknown buyers (email only) get a draft with an invitation. If finalization fails, the order is saved as a visible draft for admin review.

Params

channelIdstringrequiredChannel _id
dropIdstringrequiredDrop _id
itemsstringrequiredJSON array: [{"productId":"...","quantity":2}]. Quantity in display units: kg for weight, L for volume, count for pieces.
entityIdstringBuyer entity _id (known buyer)
emailstringBuyer email — only use when admin explicitly provides it. NEVER fabricate or guess an email. Prefer entityId from listCustomers.
buyerNamestringDisplay name (used in invite flow)
sendInvitationbooleanSend invitation email for unknown buyer (default: false — must be explicitly set to true only after admin confirms)
deliveryPricenumberCustom delivery price (0 for free). Omit for channel default.
draftOnlybooleanSave as draft without finalizing. REQUIRED when creating orders from image/document parsing (handwritten forms, PDFs, Excel). The admin must review and finalize manually.
{
  "tool": "createOrder",
  "params": {
    "channelId": "channel_abc123",
    "dropId": "drop_abc123",
    "items": "..."
  }
}

{ order: { orderId, orderNumber, status }, invitation?: { invitationId } }

estimateOrder

Compute order totals (items + delivery + grand total) without creating an order. Use this to show a breakdown before confirming. Returns stock availability per item — check hasOverstock and warn the user about insufficient inventory before proceeding.

Params

channelIdstringrequiredChannel _id
dropIdstringrequiredDrop _id
itemsstringrequiredJSON array: [{"productId":"...","quantity":2}]. Quantity in display units: kg for weight, L for volume, count for pieces.
entityIdstringBuyer entity _id (for resolving buyer name)
deliveryPricenumberCustom delivery price (0 for free). Omit for channel default.
{
  "tool": "estimateOrder",
  "params": {
    "channelId": "channel_abc123",
    "dropId": "drop_abc123",
    "items": "..."
  }
}

{ estimate: { buyerName, channel, items: [{ ..., availableStock, requestedStock, overstock }], subtotal, delivery, deliveryOverridden, grandTotal, currency, taxType, hasOverstock } }

channels

listChannels

List all channels for your shop

{
  "tool": "listChannels"
}

{ channels: Channel[] }

getChannel

Get a single channel by ID

Params

channelIdstringrequiredThe channel _id
{
  "tool": "getChannel",
  "params": {
    "channelId": "channel_abc123"
  }
}

{ channel: Channel }

createChannel

Create a new sales channel

Params

titlestringrequiredChannel title
privacystring"public" or "private" (default: public)
paymentsstringPayment method: "invoice", "manual_invoice", "swish", or "card"
deliveryTypestring"delivery" or "pickup"
deliveryPricenumberDelivery fee
{
  "tool": "createChannel",
  "params": {
    "title": "Organic Tomatoes"
  }
}

{ channel: Channel }

updateChannel

Update a channel. Only send fields you want to change.

Params

channelIdstringrequiredThe channel _id
titlestringChannel title
privacystring"public" or "private"
paymentsstringPayment method
deliveryTypestring"delivery" or "pickup"
deliveryPricenumberDelivery fee
{
  "tool": "updateChannel",
  "params": {
    "channelId": "channel_abc123"
  }
}

{ channel: Channel }

deleteChannel

Delete a channel. Fails if drops reference this channel.

Params

channelIdstringrequiredThe channel _id
{
  "tool": "deleteChannel",
  "params": {
    "channelId": "channel_abc123"
  }
}

{ deleted: true }

shop

getShop

Get your shop details

{
  "tool": "getShop"
}

{ shop: Shop }

updateShopProfile

Update the shop's public profile. Only send fields you want to change: title, description, contact info, social links, certifications.

Params

titlestringShop title (1-100 chars)
descriptionstringShop description
contactPersonstringContact person name
contactPhonestringContact phone number
contactEmailstringContact email address
instagramstringInstagram URL
facebookstringFacebook URL
websitestringWebsite URL
certDemeterbooleanDemeter certification
certKravbooleanKRAV certification
certOrganicbooleanEU organic certification
certBiosuissebooleanBio Suisse certification
certAbbooleanAB (Agriculture Biologique) certification
certOtherstringOther certification (free text)
{
  "tool": "updateShopProfile",
  "params": {}
}

{ success: true }

updateShopLocalisation

Update shop localisation settings: language, country, currency, tax rate and mode. Only send fields you want to change.

Params

languagestringShop language: 'en' or 'sv'
countrystringCountry code (e.g. 'SE', 'DK')
currencystringCurrency: 'sek', 'dkk', 'eur', or 'usd'
taxnumberDefault tax rate as decimal (e.g. 0.12 for 12%)
taxModestringTax mode: 'manual' or 'country_rules'
{
  "tool": "updateShopLocalisation",
  "params": {}
}

{ success: true, language?: string }

updateShopSlug

Change the shop's URL handle (slo.earth/<handle>). Must be unique, lowercase letters/numbers/hyphens only.

Params

slugstringrequiredNew URL handle (e.g. 'nibble-farm')
{
  "tool": "updateShopSlug",
  "params": {
    "slug": "..."
  }
}

{ success: true, slug: string }

togglePickup

Enable or disable pickup at the shop's physical address.

Params

enabledbooleanrequiredtrue to enable pickup, false to disable
{
  "tool": "togglePickup",
  "params": {
    "enabled": false
  }
}

{ success: true, pickupEnabled: boolean }

updatePhysicalStore

Configure the shop's physical store walk-in opening hours and Google sync. Opening hours is display-only metadata (the farm-shop walk-in story); online ordering windows are configured separately via order schedules. Opening hours is an array of 7 objects (day 0=Monday to 6=Sunday) with open/close times and closed flag.

Params

enabledbooleanrequiredEnable/disable physical store
openingHoursarrayrequiredArray of {day:0-6, open:'HH:MM', close:'HH:MM', closed:boolean}
googleSyncEnabledbooleanEnable Google Maps hours sync
{
  "tool": "updatePhysicalStore",
  "params": {
    "enabled": false,
    "openingHours": []
  }
}

{ success: true }

updateShopColors

Update the shop's brand colors. Only send colors you want to change. Values should be valid CSS color values (hex, rgb, etc.).

Params

bgstringBackground color (e.g. '#ffffff')
fgstringForeground/text color (e.g. '#1a1a1a')
secondarystringSecondary text color
accentstringAccent/brand color
{
  "tool": "updateShopColors",
  "params": {}
}

{ success: true }

updateSwishConfig

Configure Swish payment (Swedish mobile payment). Set the Swish phone number and company name.

Params

phonestringrequiredSwish phone number (Swedish format, e.g. '0701234567' or '1231234567')
companystringrequiredCompany name shown to payers
{
  "tool": "updateSwishConfig",
  "params": {
    "phone": "...",
    "company": "..."
  }
}

{ success: true }

publishShop

Publish or unpublish the shop

Params

publishbooleanrequiredtrue to publish, false to unpublish
{
  "tool": "publishShop",
  "params": {
    "publish": false
  }
}

{ shop: Shop }

createOrderSchedule

Create an order schedule (producer-facing: 'schedule'). Restricts when customers can order and which delivery methods / pickup options apply. Multiple schedules coexist for parallel sales streams. Provide a TimeWindow for `timeWindow` (when buyers can order) and `deliveryTimeWindow` (when fulfillment happens).

Params

titlestringOptional producer-facing name, e.g. 'Restaurant week'. If omitted, the UI auto-labels the schedule from its contents.
timeWindowobjectTimeWindow tagged union: { kind: 'anytime' } | { kind: 'recurring', rrule, durationDays, hours, interval?, offsetDays? } | { kind: 'oneoff', start, end, hours }.
deliveryTimeWindowobjectTimeWindow for fulfillment timing.
timeWindowExceptionsarrayPer-date overrides: [{ date, action: 'skip'|'open'|'note', note?, overrideHours?, overrideDates? }].
appliesToStorefrontbooleanSchedule applies to consumer (storefront) buyers.
appliesToWholesalebooleanSchedule applies to wholesale (B2B) buyers.
channelIdsarrayChannel _id[] — which lists this applies to. Omit or empty = applies to all lists.
deliveryMethodIdsarrayDelivery method _id[] offered by this schedule.
pickupOptionIdsarrayPickup option _id[] offered by this schedule.
prepLeadDaysnumberBusiness days between order and earliest bookable slot. Default 0.
sortOrdernumberDisplay order (lower = earlier). Optional.
{
  "tool": "createOrderSchedule",
  "params": {}
}

{ orderSchedule: OrderSchedule }

updateOrderSchedule

Update an order schedule. Only include the fields you want to change. Arrays (channelIds, deliveryMethodIds, pickupOptionIds) replace the stored list when provided. timeWindow / deliveryTimeWindow are full TimeWindow objects.

Params

orderScheduleIdstringrequiredThe orderSchedule _id
titlestringNew title
timeWindowobjectReplacement TimeWindow for the order side.
deliveryTimeWindowobjectReplacement TimeWindow for delivery.
timeWindowExceptionsarrayReplacement exception list.
appliesToStorefrontbooleanSchedule applies to storefront buyers.
appliesToWholesalebooleanSchedule applies to wholesale buyers.
channelIdsarrayReplaces channels. Empty array = applies to all lists.
deliveryMethodIdsarrayReplaces delivery methods.
pickupOptionIdsarrayReplaces pickup options.
prepLeadDaysnumberBusiness days between order and earliest bookable slot.
sortOrdernumberDisplay order (number) or null to clear.
{
  "tool": "updateOrderSchedule",
  "params": {
    "orderScheduleId": "..."
  }
}

{ orderSchedule: OrderSchedule }

delivery

createDeliveryMethod

Create a shop-global delivery method (delivery area + schedule + pricing). Drops filter which of these methods' slots to offer — they do not own delivery config.

Params

titlestringrequiredMethod title, e.g. 'Söder delivery'
geoAreaLabelstringrequiredFree-form area label, e.g. 'Söder', 'Stockholm inner city'
descriptionstringOptional description shown to buyers
b2cPricenumberFee (incl. VAT) for b2c buyers — what the private customer pays. Omit if not offered to b2c.
b2cFreeOvernumberFree delivery for b2c when order total (incl. VAT) >= this amount
b2bPricenumberFee (excl. VAT) for b2b buyers. Omit if not offered to b2b.
b2bFreeOvernumberFree delivery for b2b when order total (excl. VAT) >= this amount
recurrencestringRRULE, e.g. 'FREQ=WEEKLY;BYDAY=TH'. Omit for one-off.
defaultCapacitynumberDefault max orders per slot (integer). Omit for unlimited.
{
  "tool": "createDeliveryMethod",
  "params": {
    "title": "Organic Tomatoes",
    "geoAreaLabel": "..."
  }
}

{ deliveryMethod: DeliveryMethod }

updateDeliveryMethod

Update a shop-global delivery method. Only send fields you want to change.

Params

deliveryMethodIdstringrequiredThe deliveryMethod _id
titlestringMethod title
geoAreaLabelstringFree-form area label
descriptionstringDescription shown to buyers
b2cPricenumberFee (incl. VAT) for b2c buyers — what the private customer pays
b2cFreeOvernumberFree delivery for b2c when order total (incl. VAT) >= this amount
b2bPricenumberFee (excl. VAT) for b2b buyers
b2bFreeOvernumberFree delivery for b2b when order total (excl. VAT) >= this amount
recurrencestringRRULE (e.g. 'FREQ=WEEKLY;BYDAY=TH')
defaultCapacitynumberDefault max orders per slot
{
  "tool": "updateDeliveryMethod",
  "params": {
    "deliveryMethodId": "..."
  }
}

{ deliveryMethod: DeliveryMethod }

archiveDeliveryMethod

Soft-delete a delivery method (archived = true). Existing slots and historical orders keep resolving; the method is hidden from new bookings.

Params

deliveryMethodIdstringrequiredThe deliveryMethod _id
{
  "tool": "archiveDeliveryMethod",
  "params": {
    "deliveryMethodId": "..."
  }
}

{ archived: true }

pickup

createPickupOption

Create a shop-global pickup option (location + schedule + pricing). Independent of shop.physicalStore. Drops filter which of these options' slots to offer.

Params

titlestringrequiredPickup option title, e.g. 'LIVS Söder'
locationobjectrequiredAddress object { street, city, postalCode, country, placeId?, lat?, lng?, ... }
descriptionstringOptional description shown to buyers
b2cPricenumberFee (incl. VAT) for b2c buyers — what the private customer pays. Typically 0. Omit if not offered to b2c.
b2cFreeOvernumberFree over threshold (incl. VAT)
b2bPricenumberFee (excl. VAT) for b2b buyers. Omit if not offered to b2b.
b2bFreeOvernumberFree over threshold (excl. VAT)
recurrencestringRRULE (e.g. 'FREQ=WEEKLY;BYDAY=TH,SA;BYHOUR=15')
defaultCapacitynumberDefault max orders per slot
{
  "tool": "createPickupOption",
  "params": {
    "title": "Organic Tomatoes",
    "location": {}
  }
}

{ pickupOption: PickupOption }

updatePickupOption

Update a shop-global pickup option. Only send fields you want to change.

Params

pickupOptionIdstringrequiredThe pickupOption _id
titlestringPickup option title
locationobjectAddress object
descriptionstringDescription shown to buyers
b2cPricenumberFee (incl. VAT) for b2c buyers — what the private customer pays
b2cFreeOvernumberFree over threshold (incl. VAT)
b2bPricenumberFee (excl. VAT) for b2b buyers
b2bFreeOvernumberFree over threshold (excl. VAT)
recurrencestringRRULE
defaultCapacitynumberDefault max orders per slot
{
  "tool": "updatePickupOption",
  "params": {
    "pickupOptionId": "..."
  }
}

{ pickupOption: PickupOption }

archivePickupOption

Soft-delete a pickup option. Existing slots and historical orders keep resolving; hidden from new bookings.

Params

pickupOptionIdstringrequiredThe pickupOption _id
{
  "tool": "archivePickupOption",
  "params": {
    "pickupOptionId": "..."
  }
}

{ archived: true }

slots

addSlotException

Add an exception to a delivery method's or pickup option's calendar on a specific date — 'skip' (hide a recurrence occurrence), 'added' (add an extra occurrence), or adjust capacityOverride / notes. Materializes the slot doc if needed.

Params

methodOrOptionIdstringrequiredThe deliveryMethod or pickupOption _id
kindstringrequired'dm' for delivery method, 'po' for pickup option
datestringrequiredISO date or datetime of the occurrence (e.g. '2026-05-07' or '2026-05-07T15:00:00Z')
actionstring'skip' to hide, 'added' for an ad-hoc occurrence outside the recurrence. Omit to only patch capacity/notes.
capacityOverridenumberOverride the method/option defaultCapacity for this specific slot. Integer >= 0.
notesstringProducer-facing free text (e.g. 'Use back entrance, buzz #3')
{
  "tool": "addSlotException",
  "params": {
    "methodOrOptionId": "...",
    "kind": "...",
    "date": "..."
  }
}

{ slot: Slot }

removeSlotException

Remove a slot exception. Fails if orders are already booked in the slot. If safe, the slot doc is deleted and any recurrence occurrence reappears automatically.

Params

slotIdstringrequiredThe slot _id (format: 'slot.dm|po.<methodOrOptionId>.<YYYY-MM-DD>')
{
  "tool": "removeSlotException",
  "params": {
    "slotId": "..."
  }
}

{ removed: true }

customers

listCustomers

List customers (buyers/followers) for your shop. Returns company name and channel membership for each buyer. Searches entity title, org name, and company name.

Params

searchstringSearch by name, company name, or email
limitnumberMax results (default: 50)
{
  "tool": "listCustomers",
  "params": {}
}

{ customers: Customer[] } // Each: { id, displayName, companyName, email, channels: [{_id, title}] }

updateFollowerChannels

Update which channels a buyer has access to

Params

entityIdstringrequiredBuyer entity _id
channelIdsarrayrequiredArray of channel _ids the buyer should have access to
{
  "tool": "updateFollowerChannels",
  "params": {
    "entityId": "...",
    "channelIds": [
      "channel_abc123"
    ]
  }
}

{ updated: true }

inviteBuyer

Invite a buyer to follow your shop. ONLY call this after: 1) confirming the buyer is not already a follower (listCustomers), 2) the admin has provided the email, 3) you have read the email back and the admin confirmed it.

Params

emailstringrequiredBuyer email — must come from the admin, NEVER fabricated or guessed
channelIdsarrayOptional: array of private channel _ids to grant access to
{
  "tool": "inviteBuyer",
  "params": {
    "email": "..."
  }
}

{ invitation: { invitationId } }

notifications

notifyFollowers

Send a push notification to all shop followers

Params

titlestringrequiredNotification title
bodystringrequiredNotification body text
{
  "tool": "notifyFollowers",
  "params": {
    "title": "Organic Tomatoes",
    "body": "..."
  }
}

{ sent: true, count: number }

meta

listTools

List all available API tools and their parameters

{
  "tool": "listTools"
}

{ tools: ToolDefinition[] }

reportTrainingFeedback

Log a training correction from the admin for the developer AI to process. Use when the admin corrects your response or teaches you a rule.

Params

userMessagestringrequiredThe original user prompt that led to the incorrect response
aiResponsestringrequiredBrief summary of what you responded (the wrong/improvable answer)
correctionstringrequiredThe admin's correction or instruction in their own words
categorystringClassification: prompt-rule, tool-behavior, product-matching, tone, format, bug, or other
{
  "tool": "reportTrainingFeedback",
  "params": {
    "userMessage": "...",
    "aiResponse": "...",
    "correction": "..."
  }
}

{ logged: true, id: string }

Errors

REST endpoints return HTTP status codes plus a stable JSON error shape. Mutations may include a requestId for tracing. The agent adapter returns the same underlying failures mapped into tool-call responses.

unauthorized

Invalid or revoked API key

forbidden

Key doesn't have permission for this tool

not_found

Resource doesn't exist, or unknown tool name

validation

Missing or invalid params

rate_limit

Too many requests — 60/min per key

internal

Something went wrong on our end

Rate limits

60 requests per minute per key. Check the response headers:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1708617600

For agents

If you're an AI agent: call listTools first to discover every available operation and its parameter schema.

{ "tool": "listTools" }

The response includes name, description, category, params, and return shape — everything you need.