Storage
Two kinds of data
Application-owned domain data
ctx.storage → storage-service → documents
Examples (Restaurant Pro): reservations, orders, menu, tables.
Platform-owned data
Customer Hub people, Organization workflow/inbox state, Marketing campaigns — not stored as your domain collections.
API
ctx.storage.insert(collection, document, options?: { allocate_code?: { prefix: string; start?: number } })
ctx.storage.find(collection, options?: { filter?, limit?, sort? }) // → { items, total }
ctx.storage.get(collection, id)
ctx.storage.update(collection, id, patch)
ctx.storage.delete(collection, id)
Calls POST {base_url}/v1/internal/storage/{op} with { …body, context } and optional Authorization: Bearer.
How binding is supplied
- Preferred:
platform.storageontool.invoke(base_url,token,context) - Env fallback for URL/token:
QEFRO_STORAGE_URL,QEFRO_SERVICE_TOKEN/QEFRO_INTERNAL_TOKEN
Still requires platform.storage.context on invoke — without context the SDK throws:
ctx.storage requires platform.storage.context on tool.invoke
Without base URL:
ctx.storage requires platform.storage.base_url or QEFRO_STORAGE_URL
Isolation
platform.storage.context includes tenant_id, workspace_id, installation_id, solution_id, optional identity_id, and capability strings (storage.read / write / update / delete).
Managed vs external
| Managed Marketplace App | External SDK Connection | |
|---|---|---|
| Typical | ctx.storage with install scope | Own database / ERP |
platform.storage | Injected when storage-service configured | Only if install/solution scope present |
| Manifest | May declare collections + allocate_code | N/A |
Collections (manifest hint)
Restaurant Pro example:
collections:
- name: reservations
allocate_code:
prefix: "R-"
start: 1001
Allocate codes are also available at insert time via options.allocate_code.