A shared inbox in Front mixes two kinds of text in one conversation: the messages the customer saw, and the comments the team wrote to each other. "We've refunded you" and "don't refund, they're about to churn" can sit three lines apart. An agent that blurs the two will one day tell a customer something they were never meant to hear.
The issue for this connector said it directly: keep the distinction in the graph. For Mergetober I built a Front connector for cognee around that.
I picked Front because I had just built the Zendesk connector, and the hardest part there was the same problem this issue warned about: keeping internal notes apart from what the customer was told, and making sure deletes really leave memory. Front has the same shape of problem, so I wanted to see whether what I'd learned would carry over.
- Pull request: topoteretes/cognee-community#586
- Issue: topoteretes/cognee#4734
Setting it up
An API token and the inboxes you want to sync:
import cognee
from cognee_community_connector_front import front_source
await cognee.remember(
front_source(token=token, inboxes=["inb_..."]),
dataset_name="front",
primary_key="id",
write_disposition="merge",
max_rows_per_table=0,
)
Each conversation becomes one document: subject, inbox, status, assignee and tags, then every message and comment in time order. Messages are labelled Customer (name) or Teammate (name), comments Internal comment (name). Each row also carries a visibility column (customer or customer_and_internal), and include_comments=False leaves comments out completely. Drafts are skipped, and no email address or phone number is written into a document.
What cognee answered, before and after a delete
I seeded two conversations in a Front inbox, each with an internal comment: Maria Lopez was charged twice for her October plan, and Tom Becker couldn't log in after an app update. Then I synced the inbox into cognee (OpenAI as the LLM) and asked one question that needs both the customer's message and the team's internal note:
Why was Maria Lopez charged twice and what did the team decide about it? And what should users do if they cannot log in after the update?
Maria Lopez was charged twice due to a payment retry bug, which had been fixed on October 12. The team approved a refund for one duplicate charge.
The update cleared saved sessions, so users who cannot log in should sign in again.
The cause and the decision only exist in the internal comment. The customer never saw them. That's the reason for the labels: the document says Internal comment (name), so an agent that drafts a customer reply can tell what was said to the customer and what the team said among themselves.
Here's the conversation in Front, with Maria's message and the internal comment:
Then I deleted it in Front. A trashed conversation stays restorable for 30 days, but it shouldn't stay in an agent's memory:
After the next sync (scanned 0, deleted 1), the same question:
The context does not explain why Maria Lopez was charged twice or what the team decided. After the update, users should sign in again because it cleared saved sessions.
The charge is forgotten, and the login answer, from a conversation nobody touched, is still there. The second sync didn't re-read anything: the trashed conversation still showed up in the listing, with status="deleted", so the connector knew what to forget without fetching any messages.
I tested the API before proposing anything
Several proposals on the issue planned to use Front's /events feed for changes and deletes. Before writing my proposal, I set up a free developer account and tried every kind of change on a test inbox.
I went in expecting the Events API to be the answer. Front records every change as an event, so a connector could just follow the feed. Then I added a comment, deleted it, and looked at the events again. The original comment event was simply gone. Deleting something in Front doesn't record a delete; it erases the history. That was the moment I stopped trusting the events feed and started designing around the listing instead.
What I found:
-
Deleting a comment or a conversation erases its earlier events instead of recording a delete, so
/eventscan't be the source of truth for deletes. -
Tag changes don't move
updated_at, but the conversation listing shows the current tag names. -
The listing isn't ordered by
updated_at, so you can't stop reading early at the last cursor. -
Each kind of removal looks different. A trashed conversation stays in the listing with
status="deleted". A permanently deleted one answers 404. A merged-away one answers 301 to the conversation it was merged into.
How it syncs
Every run reads the selected inboxes' listings in full (100 conversations a page). Only conversations whose updated_at, tag names, status or inbox changed have their messages and comments read again, and unchanged ones are skipped by fingerprint.
A stored conversation missing from a complete listing is looked up with redirects turned off. A 404 or a 301 means it's gone, and so does a 200 in an inbox you didn't select (it was moved out). The 301 is never followed: a client that follows it would store the survivor's content under the merged-away id.
Live run
On the developer account, through a real dlt merge:
| Step | Result |
|---|---|
| First sync | conversations stored, the internal comment labelled, no email addresses |
Comment edited on A, tag added to B (tags don't move updated_at) |
scanned: 2, both changes in the documents |
| B trashed, C deleted for good | deleted: 2 |
| Nothing changed | scanned: 0 |
| B restored |
scanned: 1, B is back |
| Two conversations merged in the Front app | the merged-away one answers 301: deleted: 1, scanned: 1, the survivor holds both messages |
| The sync after that | scanned: 0 |
Safety choices
- A failed inbox listing never forgets anything. It is retried next run while the other inboxes sync, and raises after three failed runs in a row.
- Rate limits depend on the company's plan, so the client paces itself from
x-ratelimit-remainingandx-ratelimit-resetinstead of a fixed number. - A conversation deleted or merged while a sync is running is skipped that run and forgotten on the next one, instead of failing the run.
- The token only goes to the API host over https, and next-page links to another host are refused.
One lesson
If you're building a connector like this, spend the first hour on a real account, not in the API docs. Add, edit, delete, merge and move things, and write down what the API actually shows after each step. Every important design decision in this connector came from that hour, and none of it was in the documentation.
What's next
Reading every listing on every run is simple and catches tag changes, but it costs one request per 100 conversations. For very large inboxes I'd like to add a periodic full read with a cheaper check in between.
The setup steps are in the README in the pull request, and examples/example.py remembers an inbox and asks it a question.




Top comments (0)