Skip to content

Ask AI

Ask anything about Destesi — setup, products, APIs.

AI answers may be wrong — always verify against the docs.

Inventory reference

Base URL: https://api.inventory.destesi.io

Three ways in.

Browser session. Signing in through SSO sets a host-only session cookie on the API host. This is what the web app uses; send it with credentials included.

Personal Access Token. For the CLI, scripts and MCP, send an identity PAT plus the workspace slug:

Authorization: Bearer idn_pat_…
X-Destesi-Workspace: acme-co

The PAT acts as its user, with that user’s workspace role and Inventory product roles. See CLI & API auth.

Integration credential. For another system, an administrator creates an integration in Settings → Connected applications and stores its one-time credential in that system’s secrets:

Authorization: Bearer inv_key_…

The credential is bound to one workspace, so workspace headers are ignored. It can run only the operations in its scopes, on the stock owners in its owner_ids. Inventory stores only a hash of it; a lost credential is replaced, never shown again. get_context returns the workspace, integration id, scopes and owners it resolved to — compare it before storing a binding.

Inventory’s API is one list of named operations, published at runtime:

Method Path Purpose
GET /v1/capabilities Version, supported and unsupported capabilities, and every operation with its input schema
GET /v1/openapi.json The same operations as an OpenAPI 3.1 document
GET /v1/query/{operation}?input={json} A list_ or get_ read. input is URL-encoded JSON
POST /v1/query/{operation} The other reads: preview_import, preview_catalog_import, read_catalog_file
POST /v1/commands/{operation} Every write

Input rules, the same on every surface:

  • The input is one JSON object. An unknown field or a null value is refused with 400 invalid_input.
  • Quantities are decimal strings ("2.5") interpreted in the item’s base unit unless unit names another.
  • Every command needs an operation_key of 1–200 characters. The same key with the same input returns the stored answer; with different input it is a 409 conflict. Keys are scoped to the caller.
  • The body is limited to 1 MiB; 8 MiB for the catalog import operations and 36 MiB for upload_catalog_file.

capabilities in the response lists: stock, units, owners, conditions, reservations, partial_fulfillment, location_lifecycle, linked_reversals, physical_returns, authenticated_context, changes, catalog_import, codes, labels, item_images, file_attachments and public_file_import. unsupported lists lots, serials, expiry_tracking and transit.

Who is what an unrestricted workspace member needs. Member means any member; admin means owner or admin. A permission in backticks is also what an integration scope or a product role must hold. A member with Inventory product roles may run reads plus exactly the permissions their roles hold; see Roles and permissions.

Operation Kind Who Notes
list_items read member Includes each item’s low_stock_at
get_item read member Archived items too
get_item_trace read member Every movement, code change and the creation, oldest first, with the balance after each
create_item command admin · items sku, name, base_unit, scale required; optional barcode is attached as a code
update_item command admin · items Rename, archive, metadata. Base unit, scale and conversions never change
preview_catalog_import read (POST) admin · items 1–500 rows; returns per-row actions and a preview_hash
apply_catalog_import command admin · items The exact reviewed rows plus preview_hash. Never touches stock
upload_catalog_file command admin · items Base64 bytes into workspace Drive, up to 25 MiB
fetch_catalog_file command admin · items A public http(s) URL into Drive
read_catalog_file read (POST) admin · items CSV, Excel, JSON or PDF into reviewable rows. PDF extraction uses the suite’s AI model
extract_catalog_image command admin · items One embedded photo into Drive
get_catalog_image read member A short-lived preview URL for a workspace image
list_codes read member Filter by item_id or location_id
get_code read member Unknown code answers found: false, not 404
get_labels read member SVG labels for 1–100 existing codes
add_code command admin · items Attach an existing printed code
generate_code command admin · items Mint code128 or ean13
remove_code command admin · items History and stock untouched

Item fields: sku, name, base_unit, scale (0–6), unit_conversions, description (≤ 8000), source_reference (≤ 200), group (≤ 200), attributes (≤ 8 pairs), listed_quantity, image_drive_file_ids (≤ 10). On update an empty string, object or array clears a field; omitting it leaves it alone.

Operation Kind Who Notes
list_locations read member
create_location command admin
update_location command admin Rename, archive, reactivate. Archive refused while holds or allocations remain
list_owners read member Workspace-owned stock is the owner with id ""
create_owner command admin
Operation Kind Who Notes
list_positions read member Filter by item_id, location_id
list_movements read member item_id, kinds, after, limit (1–500, default 100), order (asc default, desc)
get_movement read member
record_movement command by kind, below
reverse_movement command admin · reverse id, quantity, reason (≤ 200); for an issue or fulfill, optional return_location_id and return_condition (default quarantine)
get_issue_target read member · issue Validates an item/location/owner mapping and unit without revealing balances
preview_import read (POST) admin Validates up to 500 CSV opening-stock rows
apply_import command — Refused to people (422 scan_required) and not grantable to integrations

record_movement needs the permission named by its kind:

kind Needs Extra fields
receive member · receive source_reference: "scan:<code>" from a person; optional label_serials
issue member · issue
transfer member · transfer to_location_id
resize member · transfer to_item_id; method (scan/manual), code, to_code
adjust admin · adjust Signed quantity, required reason; upward needs a scan from a person
owner admin · owner to_owner_id
condition admin · condition to_condition

Every kind takes item_id, location_id, quantity, and optionally owner_id, condition (default usable), unit, reason and source_reference.

Operation Kind Who Notes
list_reservations read member Integrations see only their own
get_reservation read member Lines carry waiting, claimed, claimed_by
create_reservation command member · reserve expires_at (RFC 3339, future), 1–100 lines, optional source_reference
confirm_reservation command member · reserve Only a live hold. Integrations may send committed_at
release_reservation command member · reserve Optional lines of {id, quantity}; omit for everything left
fulfill_reservation command member · fulfill Optional lines of {id, quantity, method, code}
dispatch_reservation command member · dispatch Fulfilled or closed only; idempotent; moves no stock

A reservation created by an integration is confirmed, released and dispatched only by that integration (409 integration_owned otherwise).

Operation Kind Who Notes
get_snapshot read member Items, locations, owners, positions, reservations, settings and a cursor; publication_prices when a Shopify store is linked
list_changes read member after cursor, limit 1–500 (default 100). Returns changes and the next cursor
get_context read anyone Open to every valid integration, whatever its scopes
Operation Kind Who Notes
get_settings read member
update_settings command admin Send any of the fields below
list_alert_contacts read admin Linked Telegram chats
update_alert_contact command admin label, categories; an empty list mutes
remove_alert_contact command admin
list_integrations read admin
create_integration command admin name, scopes, optional owner_ids. The credential is in this one response
revoke_integration command admin Suite-managed integrations answer 409 integration_managed

Settings fields:

Field Values
low_stock_threshold 0–1000000 available units, default 5
low_stock_mode fixed or batch
low_stock_batch_percent 1–100, default 25; batch mode only
store_category_kind ropa, ropa_dama, ropa_caballero, ropa_nino, calzado, otro, or empty. Used for sizes when Commerce has no category
routines Up to 20 scheduled prompts for the assistant, replaced as a whole list. Inventory stores them; inventory-api does not run them

All administrator-only, and never available to an integration. Connect must be wired to the deployment (503 connect_unavailable otherwise).

Operation Kind Notes
list_shopify_stores read Stores connected in Connect, and which one is linked
get_catalog_source read The linked store, or null
link_shopify_store command account_id of a connected, active store
unlink_shopify_store command Loaded items stay; the Connect connection stays
get_shopify_import_preview read What a load would do; writes nothing. include_drafts optional
apply_shopify_import command Loads a page within a time budget; call again with next_cursor
get_shopify_push_status read Publishing on or off, paired locations, pending, failing and drifted items
enable_shopify_push / disable_shopify_push command Publishing off leaves existing Shopify products untouched
map_shopify_location command location_id, shopify_location_id; empty unpairs
set_shopify_price command item_id, price as a decimal string; empty clears
publish_shopify_stock command Quantity only, for item_ids already in Shopify; works with publishing off
sync_shopify command Publish everything pending now
Method Path Purpose
GET /healthz Liveness. Public
GET /readyz Database and schema reachable. Public
GET /v1/sso/callback SSO landing; sets the session cookie. Public
GET /v1/auth/me The signed-in user, role, and Inventory permissions
POST /v1/auth/logout Clears the session. Idempotent
GET /v1/me/products Launcher entitlements, proxied from identity
POST /v1/catalog/files Multipart upload (file) of a catalog document or photo into Drive
GET /v1/catalog/market The shop category that decides sizes: Commerce’s, else Inventory’s own
POST /v1/chat The assistant. Streams; pauses on an approval-gated operation
POST /v1/chat/resume Approve or reject the held operation (tool_use_id, approved)
POST /v1/alerts/telegram/pairing Admin. Mints the one-device Telegram link
GET /v1/alerts/preview Sample alert texts per category and language

PUT /v1/settings answers 501 use_configuration_commands; use update_settings.

Field Values
Condition usable, quarantine, damaged
Movement kind receive, issue, transfer, resize, adjust, owner, condition, fulfill, reversal, return
Reservation status held, allocated, partially_fulfilled, fulfilled, closed, released, expired
Pick method scan (with code), manual
Standard units pc, each · g, kg · ml, l · cm, m
Code kind gtin, internal, supplier, location
Generated code code128: INV- (item) or LOC- (location) plus 8 characters; ean13: GS1 prefix 2
Integration scope read, items, receive, issue, transfer, adjust, reserve, fulfill, dispatch, reverse, owner, condition
Alert category low_stock, outbound, adjustments

A position’s quantities: on_hand, held, allocated, available (usable only, never below zero), blocked (the non-usable part) and backordered (promised beyond what is on hand).

A reservation line’s quantities: quantity, held, allocated, fulfilled, released, and when relevant waiting, claimed and claimed_by. A dispatched reservation carries dispatched_at, dispatched_by and dispatched_via.

Operation errors are {"error": "code", "detail": "…"}; a permission_required body also names the permission.

Code Status Meaning
missing_session 401 No cookie and no bearer
unknown_bearer_scheme 401 Bearer is neither an identity PAT nor an integration credential
missing_workspace_header 401 PAT sent without X-Destesi-Workspace
invalid_integration 401 Unknown or revoked inv_key_ credential
admin_required 403 Needs a workspace owner or admin
permission_required 403 Your Inventory product roles do not grant this permission
forbidden_scope 403 The integration’s scopes or owners do not cover this
csrf_rejected 403 Cookie mutation from an untrusted origin
not_found 404 Unknown id — or one outside your scope or workspace
unknown_operation 404 No such operation
approval_missing 404 No pending assistant approval with that id
invalid_input 400 Malformed input, unknown field, null, bad quantity or unit
conflict 409 State conflict or reused operation_key; detail says which
insufficient_stock 409 A line cannot be picked beyond what is on hand
integration_owned 409 Only the integration that created the order may do this
integration_managed 409 A suite app’s integration cannot be revoked
item_archived / location_archived 409 New reservations refuse archived items and locations
label_already_received 409 A label serial was already received
no_catalog_source 409 Link a Shopify store first
store_not_connected / store_not_active 409 The store is not connected, or not active, in Connect
push_disabled 409 Turn on publishing to Shopify first
no_location_mapping 409 Pair an Inventory location with a Shopify location first
listed_quantity_needs_one_location 409 Items with a listed quantity need exactly one paired location
scan_required 422 Stock enters only by scanning a code of the item
quota_exceeded 429 The plan’s item limit is reached
use_configuration_commands 501 Legacy settings write
shopify_error 502 Shopify answered with an error
identity_unavailable 502 Identity could not be reached
store_unavailable 503 No database configured for this deployment
chat_disabled 503 No AI model selected for Inventory
connect_unavailable 503 Connect is not wired to this deployment
uploads_disabled 503 Drive is not wired to this deployment
market_unavailable 503 Commerce did not answer with the shop category; retry the size change
authentication_unavailable 503 The integration credential could not be checked

Quota refusals use the suite’s quota envelope rather than the flat one above. See Entitlements.

Capability Counts
max_items Items that are not archived, checked on create_item and apply_catalog_import

If the plan cannot be read, Inventory admits the write: a stock ledger does not stop because billing is unreachable. A suspended workspace is refused.

Available on Destesi’s hosted MCP server, and on any mcp binary you run with INVENTORY_API_URL set. See Inventory MCP tools. Every tool takes a workspace slug.

Tool Purpose
inventory_capabilities The live operation list and input schemas
inventory_query Run a read: list_, get_, preview_import, preview_catalog_import, read_catalog_file
inventory_command Run a write, with its operation_key

inventory_command refuses create_integration: a credential must never land in a model transcript.

dst inventory needs an identity PAT and a workspace. See CLI for install and login.

Terminal window
dst inventory capabilities # operation names and schemas
dst inventory openapi # the OpenAPI document
dst inventory list_items
dst inventory get_code --input '{"code":"7701234567890"}'
dst inventory list_movements --input '{"order":"desc","limit":20}'
dst inventory record_movement --input '{
"operation_key": "move-2026-09-30-001",
"kind": "transfer",
"item_id": "…", "location_id": "…", "to_location_id": "…",
"quantity": "3"
}'
echo '{"operation_key":"rel-1","id":"…"}' | dst inventory release_reservation --input -
dst inventory upload_catalog_file --file catalog.xlsx \
--input '{"operation_key":"upload-catalog-1"}'

The operation is the only argument; --input takes a JSON object or - for stdin (up to 8 MiB). --file works only with upload_catalog_file, for a regular file up to 25 MiB. Output is the JSON response. Pass --workspace to override the active workspace.

The base URL is derived from your configured API URL and can be overridden with --inventory-api-url or DESTESI_INVENTORY_API_URL.