⟵ work ⟶ · Client work

I worked on UpLinked for a year and a half: a carousel editor and analytics inside their Bubble app, then the rebuild on Convex with seven AI agents, a strategy planner and a phone app.

UpLinked is a LinkedIn marketing manager for entrepreneurs who are good at their trade and would rather not write about it. It works out a content strategy, interviews you about your week, writes the posts in your voice, makes the visuals, publishes them and tells you afterwards what worked. Bart Gommers and Thierry Crolla started it on Bubble, and I worked with them from spring 2025 until September 2026.

That makes it three products, really. First I built the pieces Bubble could not do, a carousel editor and an analytics dashboard, and embedded them in the Bubble app. Then a 2.0 prototype on Convex that ran inside Bubble. Then the whole thing again from the schema up: a React app on the web, on iOS and on Android, in front of 181 Convex tables, 27 cron jobs and seven AI agents, in Dutch and English. Bart built most of the front end of that himself, Thierry worked on the agents and the interview, Daan on the front end and analytics. I did the backend, the agents, the prompt system, the migration and the billing.

The UpLinked app on a desktop. A sidebar with Write, To do’s, Posts, Planning, Strategy, Intelligence and Analytics. The main panel says “How do you want to write today?” and offers two cards: Follow Your Strategy, with the next planned posts listed by date, and Free Write.
Where you start: write what the strategy planned, or whatever is on your mind.
The interview screen. The agent asks which part of the content process to document for Monday’s post. On the right, this week’s three planned posts, the first one active with its brief: a bottom-of-funnel post documenting your own process.
The interview, one planned post at a time.
Three versions of a generated LinkedIn post shown as stacked cards, “Version 2 of 3” underneath with arrows and an Edit button, and the agent saying the three versions are ready.
Three versions of every post, each written by a different model.
The editor. On the left the Refine Agent chatting in Dutch about how the post should end and writing three variants; on the right the post as it will look on LinkedIn, version 3, with a character count of 515 of 3,000 and the scheduled time.
Refining a post by talking to it. Every change the agent makes is a version you can go back to.

4 images — swipe or scroll sideways

A carousel editor inside Bubble

Carousels, the swipeable PDFs on LinkedIn, were the thing users wanted most and the thing Bubble could do least. So I built them an editor, Canva for LinkedIn basically: text, images, SVG shapes you can recolour, backgrounds and gradients across all slides, layers, alignment, snapping, templates, undo, and a brand kit that pulled your name, photo, headline and colours from your profile. Slides were square or 4:5, and out came a PDF or a zip of PNGs. Bethmi Jayamila and Umanga Wijesuriya built a first version in the DOM; I rewrote the canvas on Konva and took it from there.

It ran at its own address inside the Bubble page, and saved every change as JSON on the carousel record in Bubble through a small server-side proxy, so the API key never reached the browser. Three things took real work. Images uploaded to Bubble came from a CDN without the right headers, and one such image taints a canvas so nothing can be exported; the editor rewrites those addresses to the storage behind the CDN and loads them anonymously. Konva does not fit text to a box, so a binary search measures the text in a hidden element, twenty steps between 8 and 300 pixels. And a PDF made in the browser depends on the browser, so the export runs headless Chromium on the server, which opens the editor itself in an export mode and calls the same export function the button does. The PDF is what you saw on screen.

It was a very cool thing to build and the result was pretty great. Then image models got good, and in the rebuild carousels are generated: the visual agent asks Gemini for one image holding a grid of four or eight slides, the server upscales it, cuts it up and puts the slides in a PDF. Nobody drags a text box anymore.

The carousel editor. A toolbar with save, undo, redo, a 4:5 aspect ratio and slide 1 of 2; on the left a panel of SVG elements with a colour picker and shape templates; on the canvas two slides, one with a title, a rotated subtitle and a lighthouse photo, the other a gradient background with a selected red heart.
The carousel editor, with the SVG panel open.

Analytics went the same way. LinkedIn gives a personal profile no API for its numbers, but it lets you download them as an Excel file, so the dashboard read that file and drew engagement, followers, demographics and the best time to post. Bubble loaded it with one HTML block that fetched a script, and the frame reported its own height back so it sat in the page like any other element.

From a strategy to this week’s posts

In the rebuild, everything starts with a strategy. Twelve settings, things like your ticket size, how mature your market is and how comfortable you are selling, go through a fixed seven-step calculation that gives a sales cycle of two to twelve weeks, a mix of post types and a split between reaching new people and converting the ones who know you. The language model only names and describes what the sum decided. Onboarding has the first week ready in about fifteen seconds, while the setup animation plays, and writes the other eleven weeks in the background.

The strategy agent changes the calendar by sending operations, insert, update or delete, which are applied in one go with an id each, so a retried message does not add a post twice, and the whole change is one step in an undo history the team shares. It sees a month back and five ahead, the same window as the calendar, and only the latest snapshot of the calendar stays in the conversation, which keeps the prompt cacheable and cut the input by about a quarter.

Then it is Monday. You pick this week’s posts, the agent interviews you about each one until it has enough, and three models write three versions at the same time, Claude, GPT and Gemini, so you choose between real alternatives and we learn which model wins. The interview runs in the background and streams into a saved thread, so closing the tab loses nothing; a watchdog ends a stuck answer after ninety seconds and offers to try again.

The loop. Performance goes back into the strategy agent, and everything the product learns about you sits underneath, in every prompt.

The Strategy page. On the left the Strategy Assistant explaining a three-cycle plan for September to November and the three-posts-a-week rhythm; on the right a calendar for September 2026 with planned posts such as Lead Magnet, Proof, Story and Thought Leadership, each with a time and a TOFU or BOFU label.
The strategy agent and the calendar it plans, three posts a week.
The Posts list with thumbnails, titles, Posted or Draft status, likes and comments. On the right a post’s engagement per day and its timeline: draft created, scheduled, rescheduled three times, each superseded schedule struck through.
A post’s timeline. Rescheduling replaces the job; the old one is struck through and will not fire.

2 images — swipe or scroll sideways

What it knows about you

The posts only sound like you if the product knows you. Intelligence is 98 elements in five groups: who you are and your business, your message, how you write, what you have to show, and your guardrails, like whether your family may be mentioned. Nobody writes an element directly. Everything that comes in, a pasted document, an answer in the interview, something you said while refining a post, first becomes a data point, and fifteen minutes after the last change a model works out the elements from the data points again, each with a confidence and a link back to where it came from. So you can always see why the product thinks you write like that, and two things you said that contradict each other show up as a conflict instead of the newest one quietly winning.

The Refine agent has one tool of its own, to save an observation. Tell it “no em dashes, ever” and it proposes that as a card; approve it and it is a data point, and from then on every agent reads it. Data points go stale after ninety days.

The Intelligence overview: a large purple score of 68%, “Strong”, 67 of 98 elements, strong in Unique Perspective and Voice & Style, with cards for Personal and Business context and a recommended quiz.
How much the product knows: 67 of 98 elements.
The Knowledge tab, Writing Style, Voice: elements for Formality, Energy Level and Reader Relationship, each a short description of how this user writes and “Based on 3 data points”.
Writing style, each element with the data points it was derived from.
The Knowledge tab, Guardrails: Financial Openness and Client Recognizability filled in, Private Life and Loved Ones still to do.
Guardrails: what the agents may and may not write about.
The Data tab: cards of individual data points, such as “The user keeps their own MRR fairly private” and “Never use the not… but… sentence structure”, each tagged with its category and age, some marked Stale.
The data points underneath. Everything the agents believe starts here.

4 images — swipe or scroll sideways

Pictures, numbers and a reason to come back

The visual agent makes images, carousels, infographics and text carousels, and edits your own photos. Images are Gemini 3 Pro, called directly rather than through OpenRouter, because OpenRouter quietly returned them at half the size we asked for; it stays as the fallback when Google is rate limiting. Writing is free, pictures cost credits, a hundred a month on a paid plan.

For analytics a job runs every night just after midnight, fetching the public numbers of everyone’s posts from a third-party LinkedIn data API, and the Excel export still adds what only LinkedIn knows, like impressions. A post scraped from LinkedIn does not carry our id, so it is matched to its draft by an exact hash first and a MinHash similarity after, because people edit a post on LinkedIn after it went out. Top posts are ranked by how far they are above your own median; likes alone mostly measure how big your network is.

Every Monday there are a few tasks, upload your analytics, answer a quiz, check your strategy, and finishing them keeps a weekly streak going.

The Visual tab of a post. On the left the Visual Agent with suggestions such as “Make a carousel that tells a story”; on the right a choice of Image, Carousel, Edit image, and Infographic and GIF marked coming soon; 53 of 100 credits at the top.
The visual agent, and what it can make.
The media library: a grid of generated images in a black-and-white sketch style, many marked AI, and a detail panel with a large image of a man at an out-of-order LinkedIn vending machine, an alt text field and the file size.
The media library. Most of it generated.
Analytics: a heatmap of engagement by hour of the day and day of the week, highest on Tuesday at 8:00, and a bar chart of engagement per weekday peaking on Thursday.
When your posts do best, from a year of your own numbers.
Top Performing Posts: five posts with engagement, comments and an outlier factor from 8.3x down to 2.6x, next to a pie chart of reaction types.
Top posts, ranked by how far they beat your own median.
The To do’s page: “Week complete”, a row of week numbers with the current week marked by a flame, and a button to write the next post.
The weekly to-dos, done.

5 images — swipe or scroll sideways

Prompts that change without a deploy

Seven agents and a handful of one-shot generators make for a lot of prompts, and the people who know what a good LinkedIn post is are not the people who deploy code. So every prompt, tool description, model choice and post template lives in a second Convex deployment with drafts, versions and rollback, and a change reaches the app within a minute. Tool arguments stay in code, because the code depends on them; their descriptions do not. Every session is one trace in Langfuse, and a thumbs down is stored against the prompt version that earned it.

Moving everyone across

Bubble returns fifty items per API call, so the data came out as seventeen CSV exports and went in as small per-profile jobs, each linked to its old Bubble id and safe to run twice. My first go loaded it in one piece and ran out of memory. Users came back through a reactivation page and found their profiles, posts and subscription where they had left them, at the address the Bubble app used to have. The first days showed what the import had dropped, some knowledge among it, and the same map brought that across.

In your pocket

The same React app is a PWA and, wrapped in Capacitor, an app for iOS and Android with push sent straight to Apple and Google and a widget on the home screen. Apple wants its own payment for subscriptions sold in an app, so the app has no billing at all; you subscribe on the web. The microphone streams to Deepgram for live transcription with a short-lived key, and no audio is kept. Logging in with LinkedIn on the phone never puts the code in the app link: the server holds it behind a one-time handoff that expires in two minutes.

The app on a phone: “You have 3 posts to write for this week”, a week view with Documentation on Monday, Proof on Thursday and Promotional on Saturday, all pending, and a button “Get started with 3 posts”.
Monday morning on the phone.
A “Meet the Team” dialog on a phone listing five agents with coloured dot icons: Strategy Agent, Strategy Writer Agent, Free Writer Agent, Visual Agent and Refine Agent, each with one line on what it does.
The agents a user meets.
The planning calendar on a phone for October 2026, dots under the days with planned posts, Monday the 26th selected with one Documentation post at 08:45, and a bottom navigation bar with a large purple write button.
The planning, on the phone.

3 images — swipe or scroll sideways