<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mabera</title>
    <description>The latest articles on DEV Community by Mabera (@husseinadeiza).</description>
    <link>https://dev.to/husseinadeiza</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4173930%2F62531f20-f324-49e8-9af8-49ced835c0a7.jpg</url>
      <title>DEV Community: Mabera</title>
      <link>https://dev.to/husseinadeiza</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/husseinadeiza"/>
    <language>en</language>
    <item>
      <title>Giving AI Agents a Memory of Your Crisp Inbox</title>
      <dc:creator>Mabera</dc:creator>
      <pubDate>Sat, 10 Oct 2026 05:16:59 +0000</pubDate>
      <link>https://dev.to/husseinadeiza/giving-ai-agents-a-memory-of-your-crisp-inbox-4k0</link>
      <guid>https://dev.to/husseinadeiza/giving-ai-agents-a-memory-of-your-crisp-inbox-4k0</guid>
      <description>&lt;p&gt;&lt;strong&gt;Giving AI Agents a Memory of Your Crisp Inbox&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI agents forget everything. Every LLM call starts from zero — no idea what your customer asked yesterday, what your team answered, or how a bug was actually diagnosed. Cognee is an open-source memory layer that fixes this: you feed it your data, it builds a knowledge graph, and agents can then recall that knowledge across sessions.&lt;/p&gt;

&lt;p&gt;The hard part is getting the data in. Cognee has a handful of connectors, but the world runs on dozens of tools. As part of Mergetober (open-source month with Cognee), I built a Crisp connector — wiring the shared-inbox support tool into Cognee so agents can answer questions about what customers actually asked.&lt;/p&gt;

&lt;p&gt;This is the story of how it works, and a few design decisions that were less obvious than they looked.&lt;/p&gt;

&lt;p&gt;The problem: chat inboxes don't fit neatly&lt;/p&gt;

&lt;p&gt;Crisp is a shared inbox — visitors chat with your team, conversations accumulate. At first glance it's simple: read conversations, feed them to Cognee. But chat data has a shape that fights you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sessions are short and numerous. One conversation might be three messages ("how do I reset?" → "click here" → "thanks"). Ingest each message as its own node and your graph explodes into thousands of near-identical fragments.&lt;/li&gt;
&lt;li&gt;There's no delete feed. When a conversation is closed or aged out, Crisp just... stops listing it. There's no webhook saying "this was deleted."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both of these drove the design.&lt;/p&gt;

&lt;p&gt;Decision 1: one node per conversation, not per message&lt;/p&gt;

&lt;p&gt;The connector aggregates an entire conversation into a single markdown document — visitor context plus the full transcript:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Visitor: Ada · chat&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visitor:&lt;/strong&gt; I can't find my invoice&lt;br&gt;
&lt;strong&gt;Agent:&lt;/strong&gt; I've resent it to your email — check spam?&lt;br&gt;
&lt;strong&gt;Visitor:&lt;/strong&gt; Got it, thanks!&lt;br&gt;
This keeps the graph readable and matches how humans think about a support thread as one unit of meaning. It's the same call the existing Slack connector makes, and it's what the Cognee maintainers flagged in the issue itself.&lt;/p&gt;

&lt;p&gt;Decision 2: full-snapshot sync, because there's no delete feed&lt;/p&gt;

&lt;p&gt;Cognee already has a "forget-on-delete" mechanism: if a record drops out of the snapshot between two syncs, its orphan-cleanup removes it from the graph. But that only works if the connector loads a complete snapshot each run — the exact set of conversations currently visible — rather than merging in new ones.&lt;/p&gt;

&lt;p&gt;So the connector uses write_disposition="replace": every sync rewrites staging with all current conversations. A conversation deleted upstream simply isn't in the new snapshot, and Cognee forgets it. Unchanged conversations keep a stable content-hash ID, so they're not re-processed and re-cognified.&lt;/p&gt;

&lt;p&gt;The subtle trap: a partial snapshot must never land&lt;/p&gt;

&lt;p&gt;Here's the part that took the most care. Because "absent from snapshot" is interpreted as "deleted," a partial snapshot — say the API returned an error halfway through — would make Cognee forget a batch of perfectly live conversations. A flaky network would silently erase memory.&lt;/p&gt;

&lt;p&gt;So the connector treats a render or API failure as fatal: it aborts the run and leaves the previous memory intact, rather than committing a truncated snapshot. Transient errors (rate-limits, 5xx, timeouts) are retried with backoff first; only a persistent failure aborts. Getting this wrong would be the kind of bug that quietly corrupts memory for weeks before anyone notices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Incremental sync&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/topoteretes/cognee" rel="noopener noreferrer"&gt;https://github.com/topoteretes/cognee&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Re-cognifying the entire inbox on every run is wasteful. Crisp exposes updated_at on each conversation, so the connector takes an optional since= watermark and only renders conversations updated after it. Combined with the stable content-hash IDs, a daily sync only touches what actually changed.&lt;/p&gt;

&lt;p&gt;Auth: Crisp's two-part token&lt;/p&gt;

&lt;p&gt;Crisp authenticates with a two-part keypair (identifier + key) sent as HTTP Basic, plus an X-Crisp-Tier: plugin header, plus a website_id selecting the workspace. Not OAuth — a plugin token you generate from the Marketplace. Straightforward once you know the trio.&lt;/p&gt;

&lt;p&gt;Using it&lt;/p&gt;

&lt;p&gt;pip install cognee-community-connector-crisp&lt;/p&gt;

&lt;p&gt;export CRISP_IDENTIFIER="..."&lt;br&gt;
export CRISP_KEY="..."&lt;br&gt;
export CRISP_WEBSITE_ID="..."&lt;/p&gt;

&lt;p&gt;import cognee&lt;br&gt;
from cognee_community_connector_crisp import crisp_source&lt;/p&gt;

&lt;p&gt;await cognee.remember(&lt;br&gt;
    crisp_source(),          # or since= for incremental&lt;br&gt;
    dataset_name="crisp",&lt;br&gt;
    max_rows_per_table=0,    # ingest the whole inbox&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;answer = await cognee.search(&lt;br&gt;
    query_text="What did customers complain about this month?",&lt;br&gt;
    query_type=cognee.SearchType.GRAPH_COMPLETION,&lt;br&gt;
    datasets=["crisp"],&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;Then you can ask your agent "summarize the billing complaints from last week" and it actually knows — because the inbox is in its memory.&lt;/p&gt;

&lt;p&gt;Testing without a live account&lt;/p&gt;

&lt;p&gt;The test suite mocks the Crisp API (no token needed) and covers the parts most likely to break: message/conversation rendering, Crisp's page-number pagination, the document-source tagging that routes conversations through normal cognify, and the full-snapshot forget-on-delete behavior — including the case where a conversation vanishes from the listing between syncs and must be forgotten. 13 tests, all passing, plus ruff clean.&lt;/p&gt;

&lt;p&gt;What's next&lt;/p&gt;

&lt;p&gt;The connector is open as a PR against cognee-community. Once merged, "ask my Crisp inbox" becomes one crisp_source() away — and it's a template for the next chat-tool connector, because the same full-snapshot + aggregate-per-thread pattern applies to Intercom, Front, Zendesk, and the rest.&lt;/p&gt;

&lt;p&gt;If you're doing Mergetober too, the Crisp connector is a decent reference for the shape a Cognee data-source connector should take. And if you try it against your own inbox, I'd genuinely like to hear what breaks — chat APIs always have one more pagination quirk than the docs admit.&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>machinelearning</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
