DEV Community

Gauarv Chaudhary
Gauarv Chaudhary

Posted on

I connected Contentful to Cognee. Importing content was the easy part.

Built for the WeMakeDevs × Cognee Mergetober 2026.

Imagine running an online store where hundreds of products are managed in Contentful.

You want an AI assistant that can answer questions about those products using Cognee's memory.

Getting the content into Cognee is one problem. Keeping that memory correct when someone edits or deletes a product is another.

Say a product's warranty changes from two years to one. Or the product gets discontinued entirely.

If Cognee still remembers the old information, your assistant could confidently give customers the wrong answer.

That was the problem behind Cognee issue #4787.

I built a Contentful connector that imports published content into Cognee, keeps it synchronized, and removes outdated information when the original content disappears.

What it actually does

The connector lives in cognee-community/packages/connector/contentful/.

It does five things:

  1. Connects to Contentful using a Content Delivery API token.
  2. Imports entries, asset metadata, and content models as documents Cognee can understand.
  3. Syncs changes without downloading everything again.
  4. Handles deletions so removed content doesn't remain in AI memory.
  5. Keeps sources isolated so changing one selection doesn't affect another.

Content models are especially useful here.

Suppose an entry has a field called batteryLife with the value 12.

Twelve what? Hours? Days?

The content model provides the field's structure and description. Without that context, an AI system has less information to interpret the value correctly.

So the connector imports content models separately instead of just dumping entry values into documents.

One synchronization run. Entries and assets use the Sync API, while content models are fetched separately.

The tricky part: knowing when to forget

The obvious approach to synchronization is simple.

Fetch everything once, then use sys.updatedAt to find recently edited entries.

That works for updates.

But what about deletions?

A deleted entry won't appear in the normal listing anymore. A timestamp filter alone can't reliably tell Cognee which information it should forget.

That's why I used Contentful's native Sync API.

The first run imports the available content and saves a checkpoint. Later runs use that checkpoint to retrieve changes.

More importantly, Contentful also returns deletion events like DeletedEntry and DeletedAsset.

The connector turns those events into dlt hard-delete markers, allowing Cognee to clean up obsolete documents and their associated graph and vector data.

There's another detail I didn't want to get wrong: pagination.

If Contentful returns five pages and page three fails, the connector must not save the final checkpoint.

Otherwise, the next synchronization could skip changes it never processed.

So it follows every page before accepting the terminal sync token. If the response is incomplete, the run fails instead of silently losing information.

One source shouldn't delete another source's memory

Here's another situation.

Suppose one integration imports English product descriptions, while another imports articles in multiple languages.

Changing the first integration shouldn't touch the second.

I added a source_id to give each logical source its own synchronization state.

Changing filters under the same source ID triggers a selection refresh. The connector removes documents that no longer belong to that selection while preserving other sources.

Credentials are also kept separate from source identity.

Rotating an API token shouldn't mean importing the entire collection as a new source.

The failure case I cared about

A successful API request doesn't mean the whole synchronization succeeded.

Think about this sequence:

  1. Contentful returns an update or deletion.
  2. dlt successfully loads the change.
  3. Cognee fails while processing or cleaning up its stored memory.
  4. The next Contentful sync returns no new changes.

Now what?

If the connector only trusts the latest API response, that earlier failed work could remain unfinished.

The implementation relies on Cognee's retained dlt staging so a later run can reconcile existing records again, even when the provider returns an empty delta.

I also covered individual cleanup failures that Cognee logs without raising an exception.

That's the distinction that matters here: successfully loading a change isn't the same as successfully updating AI memory.

Proving it works locally

I wanted to test more than whether the connector could parse some JSON.

The important questions were:

  • Does updating an entry replace obsolete content?
  • Does deleting the final entry remove its memory?
  • Can two sources coexist without deleting each other's data?
  • Can an interrupted ingestion recover on the next run?

The October 8 local verification report recorded 94 passing tests.

Tests Coverage
87 Contentful provider behavior, synchronization, and dlt
7 Real local Cognee ingestion, storage, deletion, and recovery

The same 94 tests passed on Python 3.11, 3.12, and 3.13, plus another Python 3.12 run with the newest permitted dependencies.

Eight relevant upstream Cognee regression tests also passed, along with Ruff checks and package builds.

Local test evidence. Contentful responses and model operations were mocked; SQLite, Ladybug, and LanceDB storage paths were exercised for real.

That distinction is important.

These tests provide evidence of local correctness, including deletion from relational, graph, and vector storage. They don't yet prove authenticated Contentful lifecycle behavior.

How to try it

The implementation is available in my Contentful connector branch.

From the connector directory:

uv sync --locked
Enter fullscreen mode Exit fullscreen mode

Configure your Contentful space and Delivery token:

export CONTENTFUL_SPACE_ID="your-space-id"
export CONTENTFUL_DELIVERY_TOKEN="your-token"
export CONTENTFUL_ENVIRONMENT_ID="master"
export LLM_API_KEY="your-model-key"
Enter fullscreen mode Exit fullscreen mode

Then run:

uv run python examples/example.py
Enter fullscreen mode Exit fullscreen mode

The example synchronizes published content into Cognee and runs a search query.

You can select specific content types and languages, or disable asset ingestion. Subsequent runs synchronize changes for the same source.

For example, a product-catalog application could import descriptions, search for products matching a customer's requirements, and refresh its memory after catalog edits.

That's an illustrative use case. A live Contentful-to-Cognee demonstration is still needed to establish real-provider behavior.

What it doesn't do

A few limitations worth mentioning:

  • It reads published content, not drafts.
  • It imports asset metadata, not image or video contents.
  • It doesn't recursively expand every linked entry.
  • It doesn't include a built-in scheduler.
  • Authenticated Contentful import, update, unpublish, and deletion are not yet live-verified.

An invalid synchronization checkpoint also requires deliberate recovery rather than an automatic destructive reset.

What I'd take away from this

Importing data once is straightforward.

Keeping it correct when the source changes, an entry disappears, or processing fails halfway through is the harder problem.

The useful lesson from this connector is that synchronization needs to account for both sides: what the source says changed, and what the destination actually finished processing.

Otherwise, everything can appear to work while the AI is still remembering yesterday's information.


Code and references

Built as part of WeMakeDevs × Cognee Mergetober 2026.

Top comments (0)