Skip to main content

Platform Service Patterns

Use these patterns when choosing how an app, eai-gofer, or terminal automation should call EAI platform capabilities.

The template's tenantId field and --tenant-id option are stable contract names. Their values identify an EAI workspace.

Boundary Rules​

  • Browser code calls the app BFF at /api/eai/....
  • Browser streaming calls use /api/eai/stream/....
  • App code should prefer template hooks before hand-written fetches.
  • CLI automation may call platform services through authenticated eai commands.
  • Use eai publicapi only for authorized PublicAPI V4 routes that do not yet have named SDK or CLI support.
  • Do not generate direct downstream database, blob, search, model provider, or platform secrets.

Service Selection Matrix​

NeedApp PatternCLI PatternNotes
Frontend compositionsrc/eai.config layout slots plus src/eai.blocks.tsx registryeai gofer refresh installs guidanceKeep config data-only; callbacks belong in overrides.
Data modelObject Types in src/eai.config/object-types.tseai types validate, eai types seed, eai types diffObject Types define resource contracts.
Structured resourcesuseResources(type) or client.resourceseai resources list/get/create/update/delete/queryDefault for workspace business data.
Resource actionsclient.resources.executeAction(type, id, action)named resources command if available; otherwise eai publicapi post /v4/data/resources/...Actions enforce Object Type rules.
Resource searchhelper around PublicAPI resource search if SDK support is absenteai resources search "<query>" --mode hybridSearch is a projection over canonical data.
Resource fileshelper around resource file routeseai resources file upload/get/deleteUse for file fields on typed resource objects.
Upload documentsuseDocuments().upload(file, context)eai docs upload <file> --planning-application-id <id>One queued upload; full processing must be supported by the configured lifecycle.
Classify documentsuseDocuments().classify(files, context)eai docs classify <file> --vertical-key <app-key> --workflow-key <workflow-key>One queued ResourceAPI upload with authorised parent or paired app/workflow context.
Direct URL analysisuseDocuments().classifyByUrl(url, { verticalKey, workflowKey })eai publicapi post /v4/data/documents/classify-by-urlDirect analysis, not durable upload or saved results.
ChatuseChat(workflowId, stage).send/streameai chat send, eai chat streamUse message, conversation_id, and params.
Advanced PublicAPIBFF or server helpereai publicapi <method> /v4/...Use only when named support is missing.

Storage Backend Rules​

  • postgresql: canonical structured resource storage.
  • documentdb: document-model persistence when the data genuinely needs it.
  • blob: large files or file-like resources behind API-mediated access.
  • search: derived full-text, vector, or hybrid projection, not the sole system of record for runtime writes.

Document RAG indexing is a documents service pattern (useDocuments().ragIndex or eai docs index), not a reason to create a search-only Object Type.

Chat Payload Pattern​

await client.chat.send({
workflowId,
stage: "chat",
message: "Summarise this application",
conversationId,
params: {},
runtime_context: { applicationId },
});

The platform expects message, conversation_id, and params. Do not generate legacy payloads such as chat_input for new apps.

Resource Payload Pattern​

An Object Type has two identifiers: the PascalCase configuration/model name and the exact lowercase kebab-case persisted/transport slug. Generated linkTypes[].targetObjectType, runtime target_type, route parameters, and governed query fields contain slugs, never model names. Same-manifest names are authoring shorthand only and must be resolved before publishing. Existing stored slugs are authoritative and must not be re-derived or renamed.

Use useResources or client.resources for app calls. Do not hand-write v4 resource paths or create a local slugifier.

const { list, create, update, executeAction } = useResources<ApplicationData>(
"Application",
tenantId,
);

const applications = await list({ limit: 20, sort: "-created_at" });
const created = await create({ applicantName: "Jane", status: "draft" });
await update(created.id, { status: "submitted" }, created.version);
await executeAction(created.id, "submit");

Updates require the current version for optimistic locking.

Document Pattern​

const { classify, getJobStatus, getRecord } = useDocuments(tenantId);

const response = await classify([file], {
verticalKey: "business-docs",
workflowKey: "document-review",
});
if (!response.ok) throw new Error(`Document admission failed (${response.status})`);
const queued = await response.json();
const jobResponse = await getJobStatus(queued.jobId);
if (!jobResponse.ok) throw new Error(`Document job read failed (${jobResponse.status})`);
const status = await jobResponse.json();
// Poll this same job until terminal; do not upload the file again.
// On completion, read the retained document using its returned document_id.
// getRecord(documentId) returns persisted stages, not just queue acceptance.

The app/workflow must have a published classifier target with the configured business-document-v1 lifecycle and ready private storage. Existing DAISY and Assess callers retain their authorised planning/case context. Classification uploads once and returns a queued job, not an immediate analysis. Business full/rag processing is rejected until that lifecycle supports indexing; do not report a queued job or unsupported indexing as success.

Use resource file routes when the file is a property of a typed resource object. Use document upload, classification, and RAG routes when the platform should process the document content.

For workflow steps, AI-agent questions, and prompting guidance, see V4 Documents And Files.