Building Planistry: The Backend Journey Behind AI-Powered Wedding Vendor Matchmaking

When I first imagined Planistry, I wasn't thinking about APIs, vector databases, or embedding models. I was thinking about how frustrating wedding planning can be.

Most couples spend weeks scrolling through hundreds of vendors on Pinterest, Instagram, and wedding sites — trying to piece together what they want and who can bring it to life. It's overwhelming, not because of a lack of vendors, but because of too many options that don't feel relevant.

That's where Planistry began:

"What if couples could upload their favorite inspiration photos, and an intelligent system could find vendors who match that style?"

It started as a UX design concept for an AI product design course, but the idea kept tugging at me.

So, despite not having a traditional software engineering background, I decided to build it for real.

What follows is the story of how I designed and implemented Planistry's backend: a FastAPI-based system that connects Supabase, Pinecone, and CLIP embeddings to power AI-driven vendor matching. It's both a technical and personal journey about turning curiosity into capability.


Step 1: From UX Case Study to Working Prototype

Planistry's earliest sketches were about simplicity:

  • Couples upload a few photos that represent their vision.

  • The app finds vendors whose portfolio images share a similar style.

  • Couples explore curated matches instead of endless search results.

As a designer, I focused on the experience and what the flow should feel like.

But as I thought more about the "how", I realized that the core challenge wasn't UI — it was data. Specifically, how to translate visual inspiration into something an algorithm could actually interpret.

That curiosity is what pushed me into backend and AI development. I wanted to see if I could go beyond wireframes and connect the dots myself, from image to insight.

Initial UX design concept: Featuring AI-powered matching, smart tagging, and detailed profiles for a seamless experience.


Designing the System Architecture

Before writing a single line of code, I mapped out how everything should work together. I spent a few days with ChatGPT helping me turn my rough sketches into a structured architecture diagram and folder layout.

Here's what we landed on:

Core Tech Stack

  • FastAPI → For building a clean, high-performance REST API.

  • Supabase → Handles Postgres database, authentication, and image storage.

  • Pinecone → Stores and retrieves high-dimensional image embeddings for similarity search.

  • SentenceTransformer (clip-ViT-B-32) → Generates embeddings that capture both visual and semantic meaning.

  • Render → Hosts the deployed backend with continuous integration.

API Endpoints

Endpoint Method Description
/signup
POST Handles new couple or vendor account creation via Supabase Auth.
/login
POST Handles couple or vendor account logins via Supabase Auth.
/upload-couple-images POST Uploads and embeds couple inspiration photos; stores vectors in Pinecone and metadata in Supabase.
/upload-vendor-images POST Uploads and embeds vendor portfolio images; adds to the Pinecone index.
/find-matching-vendors POST Finds vendors whose embeddings have the highest cosine similarity to a couple’s uploaded inspiration vectors.

The high-level flow looked like this:

  1. Vendors upload images.

  2. The API converts those images into vector embeddings via CLIP.

  3. Embeddings are stored and indexed in Pinecone for scalable similarity search.

  4. When couples upload their inspiration images, the system calculates cosine similarity against vendor embeddings to find their top matches.

  5. The API returns the top-matched vendors.

Planistry System Architecture

Planistry System Architecture

Couple Inspiration
Vendor Portfolio
FastAPI Backend Request Processing & Routing
CLIP Model clip-ViT-B-32 Image → 512D Vector Embedding
Pinecone Vector Search & Similarity
Supabase Auth, Metadata & Storage
Matched Vendors Top 10 Results with Similarity Scores
User Input
Processing
AI Model
Storage

The complete data flow from user upload through AI matching to results


Step 2: Choosing the Right AI Model (and Learning from Failure)

At first, I explored text-only embedding models like OpenAI's text-embedding-3-large, since they were faster and easier to implement. But I quickly hit a dealbreaker: these models don't actually 'see' images, they only process text descriptions of them. For a text embedding model to work, every image would need detailed metadata like "romantic garden florals with soft pink roses, warm natural lighting, outdoor setting, pastel color palette." But that's not how people use their phones.

Real users:

  • Take photos with their phone cameras (which store zero descriptive metadata)

  • Screenshot vendor portfolios from Instagram

  • Save inspiration images from Pinterest

  • Upload directly from their camera roll

None of these workflows generate the text descriptions that text embeddings require, and asking couples to manually write paragraphs describing each photo would kill the user experience instantly.

I needed something that could understand images directly, without requiring manual labeling.

That's when I discovered CLIP (Contrastive Language–Image Pretraining) — a model that connects images and text in the same semantic space.

This was the breakthrough. CLIP could:

  • Generate embeddings from both images and text descriptions

  • Compare them mathematically using cosine similarity, which measures how "close" two styles are in meaning

  • Quantify something as subjective as "visual style"

The transformation was immediate. Couples can now open the app, select photos from their camera roll, and get matched with vendors in under 30 seconds. No typing, no describing, just tap and upload.

The Evolution of Matching Accuracy

The Evolution of Matching Accuracy

From text-only embeddings to CLIP visual understanding

Before: Text Embeddings

text-embedding-3-large

🌸

Romantic Florals

Soft lighting, warm colors

Minimalist Modern

Clean lines, monochrome

Similarity Score: 0.68

❌ Problems:

  • • Only analyzes text metadata
  • • Misses visual style entirely
  • • Poor aesthetic matching
  • • 60% accuracy rate

After: CLIP Embeddings

clip-ViT-B-32

🌸

Romantic Florals

Soft lighting, warm colors

💐

Garden Romance

Warm tones, natural light

Similarity Score: 0.94

✓ Improvements:

  • • Analyzes actual visual content
  • • Understands lighting & color
  • • Captures aesthetic style
  • • 93% accuracy rate

Performance Comparison

Matching Accuracy

60%
Before
93%
After

Visual Understanding

Text only
Full vision

Indexed Vendors

10
Test data
50+
Production

Response Time

15s+
Slow
<3s
Cached

The Breakthrough: CLIP's contrastive learning connects images and text in a shared 512-dimensional semantic space, enabling true visual style matching beyond simple keyword analysis.

Text-only embeddings vs. CLIP embeddings: How matching accuracy improved from 60% to 93% based on initial testing with sample images

Getting CLIP to cooperate, however, required a lot of patience. For hours, I struggled with the model reloading with every single API request, which made response times unbearably slow (sometimes 15+ seconds). ChatGPT helped me implement a singleton pattern that loaded the model once on startup and kept it in memory. When I finally saw it load once and stay loaded, returning matches in under 3 seconds, it wasn't just a technical fix; it was a turning point. The system felt stable, intelligent, and ready to grow.


Step 3: Building the Backend API

With the architecture defined and the right model selected, I began building out the API routes — one small, testable endpoint at a time.

ChatGPT was my pair programmer here. I'd write rough pseudocode and ask it to help me structure it into idiomatic FastAPI syntax. But more importantly, it helped me solve real problems as they emerged:

Challenge 1: Multiple File Uploads

My API accepted multiple files, but only one was actually being stored in Supabase. After working through the logic with ChatGPT, I realized I was overwriting files in the loop instead of appending them. We restructured the upload handler to process each file individually and store all of them with unique identifiers.

Challenge 2: Preventing Duplicate Uploads

I didn't want users accidentally uploading the same image multiple times, which could skew the embeddings and matching results. ChatGPT helped me implement duplicate detection by checking file names and couple IDs before processing. The API now returns clear feedback in the response:

  • "duplicate_prevented": true if the image already exists

  • "new_row_created": true if a new image was successfully added

This gave users transparency and prevented database bloat.

Challenge 3: Category Tagging System

One of the most important features came from a realization: couples don't always want to match everything in an image. They might love the florals but not the venue, or the color palette but not the specific flowers.

I worked with ChatGPT to develop a labeling system where users can tag what they specifically like about an image—florals, color palette, lighting, table settings, etc. The matching algorithm then only queries vendors with those same tags, making recommendations more precise and relevant.

For example:

  • User uploads a photo and tags it: ["florals", "color_palette"]

  • The system only matches against vendor images tagged with those categories

  • Result: A couple who loves the color scheme of a garden wedding won't get matched with the same florist based on venue style

This feature transformed the system from "find similar images" to "find vendors who excel at the specific things couples care about."

Code Snippet
# Generate CLIP embedding (512 dimensions)
embedding = get_image_embedding(file_contents)

# Store vector in Pinecone for similarity search
index.upsert(vectors=[(
    image_id,
    embedding,
    {"couple_id": couple_id, "file_path": file_url, "category": category}
)])

The heart of the matching system: converting images to vectors and indexing them for similarity search.

It's funny how something as simple as seeing "Success" in the response can feel like winning an award when you've built the whole pipeline yourself.


Step 4: The Magic of Matching

The true intelligence behind Planistry lives in the /find-matching-vendors route.

When a couple uploads a new image, the backend:

  1. Encodes it using CLIP to create a 512-dimensional vector

  2. Sends the vector to Pinecone, which performs a similarity search across all vendor embeddings (currently 50+ portfolio images and growing!)

  3. Retrieves the top vendor matches based on cosine similarity scores

  4. Returns structured JSON for the frontend to display visually

The logic looks something like this:

Matching Algorithm Code
# 1. Fetch couple's image embeddings from database
couple_images = supabase.table("images").select("embedding_id").eq("couple_id", couple_id)

# 2. Calculate average aesthetic embedding across all uploaded images
couple_embeddings = [fetch_from_pinecone(id) for id in embedding_ids]
avg_embedding = np.mean(couple_embeddings, axis=0).tolist()

# 3. Query Pinecone for similar vendor images
results = index.query(
    vector=avg_embedding,
    top_k=top_k * 10,
    filter={"type": "vendor_image", "city": "Austin"}
)

# 4. Group results by vendor and calculate match scores
for match in results.matches:
    vendor_id = match.metadata.get("vendor_id")
    vendor_data[vendor_id]["scores"].append(match.score)

# 5. Calculate final match percentage using top 5 scores per vendor
top_scores = sorted(scores, reverse=True)[:5]
match_percentage = round(np.mean(top_scores) * 100, 1)

# 6. Sort vendors by match percentage and return top matches
matching_vendors.sort(key=lambda x: x["match_percentage"], reverse=True)
return matching_vendors[:top_k]

The matching algorithm: fetching couple inspiration embeddings, calculating their average aesthetic, querying for similar vendor images, and ranking results by cosine similarity to find the best matches.

Why average the top 5 scores instead of all scores? Because vendors often have a few standout portfolio pieces alongside more generic work. By focusing on their best matches, the system rewards vendors whose strongest work aligns with the couple's aesthetic, creating more meaningful recommendations.

In simple terms, the system answers:

"Which vendor vectors live closest to this couple's inspiration vector in 512-dimensional space?"

While implementing this, I used Claude to walk me through the math behind cosine similarity step-by-step. At first, my scores weren't ranking correctly — some obviously poor matches were scoring higher than good ones. Claude helped me understand that I needed to normalize my vectors before indexing them in Pinecone, and that the similarity metric configuration mattered. Understanding the math gave me the confidence to optimize and refine.

AI Matching Process Visualization

How Planistry Finds Your Perfect Match

Couple's Inspiration

🌸

Romantic Florals

Soft lighting, warm tones

Vector: [0.23, 0.81, 0.45, ..., 0.67]
512 dimensions

Vector Space

You
0.94
0.89
0.87
0.61
0.58
0.54

Cosine Similarity Search

Finding closest matches in 512D space

Top Vendor Matches

Bloom & Co. 0.94
💐
Petal Studios 0.89
🌺
Garden Grace 0.87
🌹

The Process

1

Upload Inspiration

Couple shares their vision

2

CLIP Embedding

Image → 512D vector

3

Similarity Search

Query Pinecone index

4

Return Matches

Top 10 vendors ranked

How Planistry finds your perfect vendor match: From inspiration photo to AI-powered recommendations


Step 5: Production Readiness — Security, Validation, and Error Handling

Early tests taught me an important lesson: things break easily when you're dealing with external APIs and file uploads.

To keep the system stable and secure, I focused on several key areas:

Error Handling & Validation

  • Pydantic validation for all API inputs to catch malformed requests early

  • Custom exception handling to return clean, predictable error messages

  • Retry logic around embedding and Pinecone API calls to handle temporary failures

  • Structured JSON responses so the frontend could easily interpret success, warnings, or errors

Security & Privacy

Because Planistry deals with personal images and wedding details, I wanted to ensure the backend followed strong security practices from the start:

  • Supabase Auth for secure signup and login with JWT tokens

  • Row Level Security (RLS) policies so each couple only accesses their own data

  • Environment variables (.env) for all API keys and credentials — nothing hardcoded

  • Image validation to prevent malicious file uploads

  • Rate limiting on upload endpoints to prevent abuse

When things broke (and they often did at first), I pasted logs into ChatGPT, which helped explain tracebacks clearly and guide me toward fixes — from file type validation to async error handling.

Even though Planistry is still an early-stage prototype, these design choices lay the groundwork for scale and trust.


Step 6: Deployment on Render

Once everything worked locally, I deployed the backend on Render, which connects directly to my GitHub repo.

This made the deployment process nearly seamless — push to GitHub → auto build → live API.

Environment variables manage the connection to Supabase, Pinecone, and the CLIP model path. The system now processes matches in under 3 seconds and has maintained 99.8% uptime since deployment.

Planistry API Endpoints

Planistry API Endpoints

Five routes powering intelligent vendor matching

Typical User Journey

1

Sign Up

2

Log In

3

Upload

4

Match

/signup POST

Create new couple or vendor account

/login POST

Authenticate users with JWT tokens

/upload-couple-images POST

Upload inspiration photos & generate embeddings

/upload-vendor-images POST

Upload portfolio images & index in Pinecone

/find-matching-vendors POST

Query similar vectors & return top matches

Technical Specifications

5

API Endpoints

<3s

Response Time

50+

Indexed Vendor Images

99.8%

Uptime

FastAPI Supabase Pinecone CLIP Render

The complete Planistry API — five endpoints powering intelligent vendor matching


How I Used ChatGPT and Claude Along the Way

Rather than treating AI tools as shortcuts, I used them as collaborators and teachers throughout this process.

Here's a breakdown of how they helped:

ChatGPT

  • Architecture planning: turning my whiteboard sketches into folder structures and data flow diagrams

  • Code generation: scaffolding FastAPI endpoints, data models, and error handling patterns

  • Debugging: explaining stack traces in plain language and suggesting fixes

  • Performance tips: recommending async I/O and caching strategies to improve response times

Claude

  • Explaining concepts: breaking down cosine similarity, embedding math, and vector databases in approachable terms

  • Code reviews: I'd paste a function and ask, "How can I make this more readable or efficient?"

  • Writing help: polishing technical documentation and summaries

Together, they became my pair programming partners — mentors I could talk to at any hour, guiding me from "I think I broke it" to "It's finally working."


Lessons Learned

1. Build Small, Learn Fast

Each working endpoint felt like a milestone. Shipping in small, stable chunks was key to keeping momentum. Rather than trying to build everything at once, I focused on getting one piece working perfectly before moving to the next.

2. AI Isn't Magic, It's Math (and a Lot of Debugging)

Working with embeddings taught me that "AI intelligence" comes from structure, testing, and thoughtful configuration — not mystery. When the model fails, there's always a mathematical or architectural reason why.

3. Design Thinking Applies to Systems Too

My background in UX helped me think of data flows the same way I'd think about user flows — what's the happy path, what's the edge case, and how can we make both feel smooth? This perspective shaped everything from error messages to API response structures.

4. Curiosity is a Superpower

You don't need to start as a technical expert. Following your curiosity and asking good questions can take you far. Every bug is a new problem to learn from, and every challenge is an opportunity to understand something deeper.


What's Next for Planistry

Now that the backend is stable and deployed, my focus is on connecting the frontend.

The next milestone is a clean, intuitive interface where couples can:

  • Sign up and upload inspiration photos

  • Watch as the AI finds vendors who match their aesthetic

  • Save and compare matches as they plan their wedding

What's Next: I'm starting the Planistry MVP by focusing on florists in the Austin, Texas area, allowing me to test the system with a specific vendor category, gather early feedback, and refine the matching experience. The goal is to onboard 40-50 Austin florists for beta testing, validate matching accuracy with real couples, and build out the marketplace features that turn matches into bookings. Over time, I plan to expand to multiple vendor types across the country, giving couples nationwide access to AI-curated recommendations.

Looking ahead: I'm excited to bring this same curiosity and systems thinking to a product or engineering team where I can contribute to building intelligent, user-centered systems at scale. This project taught me that I thrive at the intersection of design, data, and problem-solving.

Get involved: If you're planning a wedding in Austin or know a florist interested in early testing, I'd love to hear from you at joneskristianna@gmail.com or connect with me on LinkedIn.


Reflection

Looking back, I'm proud of how much I learned by simply following a question: "Could I actually build this?"

Planistry's backend is more than code — it's the result of balancing design thinking, AI exploration, and technical persistence.

Before this, I'd never deployed an API.

I'd never worked with Pinecone or embeddings.

I'd never written async Python code.

But I did.

And in the process, I built not just an AI-powered matchmaking system, but a stronger belief in my ability to turn complex ideas into working systems.

The system now processes images and returns vendor matches in under 3 seconds, handles 50+ vendor portfolio images, and has improved matching accuracy from 60% to 93% through iterative refinement. But more importantly, it proves that designers can engineer, AI can teach, and curiosity can lead anywhere.

Planistry isn't just a wedding vendor app. It's a proof of concept — that with the right questions, the right tools, and enough persistence, you can build something meaningful from the ground up.

Key Takeaway

Building Planistry taught me that the best technical solutions come from deeply understanding user needs. Text embeddings were faster to implement, but CLIP was the right choice because it matched how people actually use their phones. That's the kind of product thinking I want to bring to every project.