# How to fill a coreAI assistant with data via the API

The coreAI API is built for systems that want to push their own data straight into an assistant and use it as the basis for answers in chat or search. The integration is first about keeping the knowledge base up to date, then about choosing how users will meet the assistant: through the coreAI widget, a custom chat interface, or plain search.

## Start with an assistant, a source, and a token

All calls to API v2 go to `https://portal.coreai.no/api/v2` in production or `https://stage.coreai.no/api/v2` in the test environment. The API uses a Bearer token, and the most important paths need both an `assistantId` and a `contentImporterId`.

`assistantId` points to the assistant that will answer. `contentImporterId` points to the API source where the data is stored. When you read entities back by external ID, coreAI looks them up in the specified source.

## Upsert is the main path for filling the assistant

For an ongoing integration, your system should send a `POST` to `/assistants/{assistantId}/sources/{contentImporterId}` every time an object is created or changed. Upsert replaces the entire entity: the first call creates it, subsequent calls update the same `id`. You don't need extra calls to check whether the entity already exists — upsert handles that for you.

```json
{
  "data": [
    {
      "id": "product-123",
      "type": "products",
      "lastModifiedAt": "2026-04-28T12:00:00Z",
      "attributes": {
        "productNumber": "123",
        "name": "Mountain jacket",
        "description": "Lightweight shell jacket for changing weather.",
        "url": "https://example.com/products/123",
        "price": 1299,
        "inStock": true
      }
    }
  ]
}
```

The entity types cover the most common data sources an assistant needs: `products`, `contents`, `documents`, `events`, `contacts`, `job_postings`, and `educations`. Each type has its own required fields. Products, for example, need `name` and `productNumber`, while content needs `name` and `longDescription`.

## Use PATCH when only parts of the entity change

`PATCH` against the same endpoint lets you send only the fields that have changed. This fits when an external system publishes small status updates, for example price, stock status, or date.

## Deleting entities

`DELETE` takes a simple list of `id` and `type`, and removes both the entity and any relationship links to it. Use it when an object should no longer be able to appear in answers — for example an expired product or a cancelled job posting.

## Custom properties make the data filterable

Some entity types can carry `properties` with short, structured values. Use camelCase names like `categoryName`, `publishedAt`, or `market`, and choose type `string`, `number`, `boolean`, or `date`. The same fields can later be used in chat and search calls with filters like `$eq`, `$gt`, `$gte`, `$lt`, `$lte`, and `$in`.

This is useful when one assistant covers several markets, product groups, or publication levels. You can fill the same knowledge base broadly, but ask the chat to answer only from a single source, market, or content type.

## Chat can be integrated at three levels

The simplest path is to use the coreAI chat widget. The widget then handles conversation ID, language, current URL, and streaming for you. For a tailored experience you can call `/assistants/{assistantId}/chat` directly and send `question`, optional `cid`, `lang`, `model`, `sources`, `resources`, `filters`, and `stream`.

If you integrate chat yourself, you must fetch the required configuration from `/assistants/{assistantId}/config`. The response gives available models, sources, and the WebSocket setup for streaming. Chat streams are published on `ai-chat.<cid>` with the events `ChatStreamProgress` and `ChatStreamUpdated` over WebSocket.

If you don't need a full conversation, `/assistants/{assistantId}/search` can be used as a knowledge-base search. It returns hits, tabs per type, and optionally an AI-generated summary. Streamed summaries use the channel `ai-summary.<uuid>` and the event `SummaryUpdated`.

## A good integration is a synchronization, not a one-off import

The robust model is to let the source system own the truth and let coreAI be the searchable, conversation-ready copy. Send updates with stable external IDs, use `lastModifiedAt`, delete objects that should no longer produce answers, and add properties that make filtering possible. The assistant then gets fresh data, traceable sources, and a chat interface that can be built as simply or as specialized as your product requires.