Lupokits API
Add the Lupokits marketing studio to any website, or drive it from your backend. Partners such as brokerages, CRMs and media platforms can open a pre-filled studio for any listing and receive finished designs back.
Overview
- Embed SDK: one script tag puts the full editor (templates, kits, AI copy, video) in a modal or container on your site. It works on phones too: the design fills the screen and the tools slide up from the bottom. Finished PNGs come back to your page.
- REST API: base URL
/api/v1. JSON in, JSON out. - Design sessions: create a pre-filled session from your server and send the agent a link, or embed it.
- Retention: listings, design sessions and uploaded files are kept for 7 days, then deleted (see data retention). Store finished designs on your side.
Embed in 2 minutes
<script src="/embed.js" data-key="pk_live_YOUR_PUBLIC_KEY"></script>
<button id="kit">Create marketing kit</button>
<script>
document.getElementById('kit').onclick = () => Lupokits.open({
listing: {
status: 'Just Listed',
address: '87 Willow Lane', city: 'Westport, CT 06880',
price: '$1,185,000', beds: '5', baths: '3', sqft: '3,050',
photos: ['https://cdn.yoursite.com/listing/1.jpg', 'https://cdn.yoursite.com/listing/2.jpg'],
},
brand: { name: 'Jamie Lee', phone: '(555) 010-2030', brokerage: 'Oakline Realty', logo: 'https://…/logo.png' },
kit: 'social-pro',
onExport(files) {
// files: [{ name, blob, type, format, channel, width, height }]
files.forEach(f => upload(f.blob, f.name));
},
});
</script>
Try it on the live partner demo page.
Authentication
| Key | Where | How to send |
|---|---|---|
pk_live_… public key | Browser (embed SDK) | data-key attribute or X-Api-Key header. Only works from the allowed origins registered for the key; the studio refuses to be framed anywhere else (CSP frame-ancestors). |
sk_live_… secret key | Your server only | Authorization: Bearer sk_live_…. Never ship it to a browser. |
Keys are made in Admin → Partner API (see partner keys). To check which key a request uses, call GET /api/v1/whoami.
Lupokits.open(options)
| Option | Description |
|---|---|
key | Public key (defaults to the script tag's data-key). |
listing | Listing object (see below). Photos must be publicly reachable URLs. |
brand | Agent / brokerage branding shown on every design. |
kit | essentials (5 designs), social-pro (12) or complete (19). Omit to open a single design. |
campaign | just-listed, coming-soon, open-house, price-improved, under-contract, just-sold. |
template, theme, format | Optional style. IDs from the catalog endpoints. |
container | Selector or element to render inline instead of a modal. |
session | Open a server-created design session. |
onExport(files, data) | Called when the user clicks Send to …. data.captions contains the caption, hashtags and description. |
onReady(), onClose() | Lifecycle callbacks. |
Returns { close(), frame }. Sites built before the rename can keep calling ListingKit.open().
Listing & brand objects
listing = {
status, headline, tagline, address, city, price, oldPrice,
beds, baths, sqft, lot, year, garage, propertyType,
description, features: ['Ocean views', …], cta, url,
openHouse: { date: '2026-11-15', start: '12:00', end: '15:00' },
photos: ['https://…'], caption, hashtags: ['#…']
}
brand = { name, title, phone, email, website, brokerage, license,
photo, logo, primary: '#1E5A66', accent: '#E09F6B', mode: 'light' | 'dark',
displayFont, bodyFont }
displayFont and bodyFont take any of the 53 font families from GET /api/v1/fonts, for example 'Playfair Display' and 'DM Sans'.
Messages & security
The SDK and studio talk over postMessage: the studio sends listingkit:ready, your page answers with listingkit:init (listing, brand, kit…), and finished files come back as listingkit:export. The studio only accepts init from origins on your key's allow-list, and only posts exports back to that origin.
Templates, formats, themes & kits
21 sizes (posts, stories, reels, banners, email and print), 12 color themes and 53 fonts ({ family, kind }, where kind is serif, sans, display or script).
The template library partners can use: every public template (a layout in a theme, or a custom design) with id, name, layoutId, themeId, category and the sizes it supports. GET /api/v1/catalog/:id adds the saved design for custom templates.
Plans, prices and what each includes, as shown on the pricing page.
curl /api/v1/kits
{ "packages": [{ "id": "social-pro", "name": "Social Pro", "items": [{ "format": "ig-square", "role": "hero" }, …] }, …],
"campaigns": [{ "id": "open-house", "status": "Open House", "style": "open-house" }, …],
"statuses": ["Just Listed", "Coming Soon", …] }
Import a listing from a link
Reads public listing metadata (schema.org JSON-LD, Open Graph, embedded page data) and the photo gallery from a property website.
curl -X POST /api/v1/listings/import \
-H "Authorization: Bearer sk_live_…" -H "Content-Type: application/json" \
-d '{"url":"https://example-media.com/sites/87-willow-lane"}'
{ "source": { "url": "…", "host": "…", "platform": "Aryeo" },
"listing": { "address": "87 Willow Lane", "city": "Westport, CT 06880", "price": "$1,185,000",
"beds": "5", "baths": "3", "sqft": "3,050", "year": "1998", "description": "…" },
"photos": ["https://…/1.jpg", "https://…/2.jpg", …],
"contact": { "phone": "+15550102030", "email": "agent@…" },
"confidence": 0.9, "warnings": [] }
422 blocked with suggestions. For production, rely on the property website from your media delivery platform or a licensed MLS feed (see data sources).AI listing copy
{ "listing": { "address": "87 Willow Lane", "city": "Westport, CT 06880", "beds": "5", "baths": "3",
"features": ["Wraparound porch", "Renovated kitchen"] },
"tone": "warm", // luxury | warm | modern | professional | playful
"length": "medium", // short | medium | long
"emojis": true, "status": "Open House", "notes": "New roof in 2024" }
→ { "source": "ai", "model": "claude-opus-5-5",
"copy": { "headline", "tagline", "description", "highlights": [], "instagramCaption",
"hashtags": [], "storyText", "emailSubject" } }
Copy uses only the facts you send and follows US Fair Housing language rules. Without an AI key on the server, a built-in writer answers instead with "source": "template".
Whether Claude is connected: { "provider": "anthropic", "model": "claude-opus-5-5", "mode": "ai" | "template" }. AI-designed templates are part of the Lupokits app (Alpha plan) and are not exposed through the partner API.
Design sessions
curl -X POST /api/v1/sessions \
-H "Authorization: Bearer sk_live_…" -H "Content-Type: application/json" \
-d '{"listing":{"address":"87 Willow Lane","photos":["https://…"]},"brand":{"name":"Jamie Lee"},"kit":"complete","campaign":"open-house"}'
{ "id": "ses_…",
"editorUrl": "/studio?session=ses_…",
"embedUrl": "/studio?embed=1&key=pk_live_…&session=ses_…" }
Send agents the editorUrl (for example from your CRM when a listing goes live), or pass session to Lupokits.open(). Sessions are deleted 7 days after they are created; after that GET and PATCH answer 404 not_found and the links stop working.
Uploads & images
Send a JPEG, PNG, WebP or MP4 as the raw request body (with its Content-Type). Returns { "url": "…", "bytes": 123456 }. Uploaded files are deleted after 7 days.
Image proxy so photos from other domains can be drawn and exported without CORS problems. Private and internal addresses are refused.
Errors & limits
Errors return JSON: { "error": "blocked", "message": "…", "suggestions": [] }. Rate limits (per key or IP, per minute): import 20, AI 15, uploads 120, image proxy 400. Exceeding them returns 429 with Retry-After.
Health & key check
{ "ok": true, "ai": { "provider": "anthropic", "model": "claude-opus-5-5", "mode": "ai" } }
curl /api/v1/whoami -H "Authorization: Bearer sk_live_…"
{ "type": "secret", "key": { "name": "Oakline Realty", "plan": "partner" } }
A public key answers "type": "public". Without a key it answers { "type": "anonymous" }; a wrong or switched-off key, or a public key used from a site not on its list, answers { "type": "invalid", "reason": "…" }.
Partner keys
Partner keys are set up by the Lupokits team. To get yours, email admin@lupokits.com.
Create a key per partner in Admin → Partner API: name the partner, list the websites allowed to embed the studio, and set the brand name and color shown in the studio. The public key (pk_live_…) goes in the embed script; the secret key (sk_live_…) is shown once and stays on the partner's server. Usage per month is shown next to each key, and keys can be switched off at any time. On a local copy, a demo key is created on first run and shown at the top of this page.
Data retention
Lupokits keeps working files for 7 days:
- Listings in the Lupokits app move to the archive 7 days after they are created. Their photos, designs and files are deleted everywhere; only the listing's name and address stay, in the Archive section of Listings. Each listing shows how many days it has left.
- Design sessions and their data are deleted 7 days after they are created.
- Uploads sent to
/api/v1/uploadsare deleted after 7 days.
Send finished files to your own storage with onExport or by downloading them. Self-hosted servers can change the period with LISTING_DAYS.
Listing data sources
Link import works with single-property websites (Spiro, Aryeo, HD Photo Hub, Tonomo), brokerage and IDX pages that publish standard metadata. For fully automatic, licensed data, connect your MLS through the RESO Web API (via your MLS's provider) and create design sessions when listings change. Only import listings you are authorized to market.
Self-hosting
| Variable | Purpose |
|---|---|
PORT | HTTP port (default 4310). |
PUBLIC_URL | Public origin used in returned URLs, e.g. https://studio.yourdomain.com. |
ANTHROPIC_API_KEY | One key for the whole site: turns on Claude for AI listing copy and AI-designed templates. Put it in the .env file and restart. AI_MODEL and AI_EFFORT are optional. |
AI_DESIGN_EFFORT, AI_REVIEW_ROUNDS | Optional: how hard Claude works on an AI template (default high) and how many times it looks at its own render and improves it (default 1). |
CHROME_PATH | Optional: Chrome or Edge for rendering AI drafts so Claude can review them. Found automatically on most machines. |
LISTING_DAYS | Optional: how many days listings, design sessions and uploads are kept (default 7). |
SMTP_PASS (SMTP_HOST, SMTP_PORT, SMTP_USER, MAIL_FROM) | Account emails: sign-up confirmation links and password reset links. For lupokits.com only SMTP_PASS is needed, the password of the admin@lupokits.com mailbox at GoDaddy; the others point it at another mailbox. |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET | "Continue with Google" on the login and sign-up pages, from a Google Cloud OAuth client (web application) with the redirect URI https://YOUR-DOMAIN/auth/google/callback. |
ADMIN_EMAIL, ADMIN_PASSWORD | The first admin account (otherwise one is created and its password written to data/admin-login.txt). Changing ADMIN_PASSWORD later gives that admin the new password on the next start. |
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_PRICE_PRO, STRIPE_PRICE_ALPHA | Subscriptions. Any plan can use STRIPE_PRICE_<PLAN ID> or a price ID set in Admin → Pricing; custom plans without one are charged at their monthly price (STRIPE_CURRENCY, default usd). Without Stripe keys, upgrades use a demo checkout outside production (ALLOW_DEMO_BILLING=1 allows it in production). |
RENTCAST_API_KEY | Optional: fills beds/baths/sq ft from an address. |
TRUST_PROXY=1 | Behind a load balancer, so rate limits see real client IPs. |
DATA_DIR, UPLOAD_DIR | Where the database and uploaded photos are stored. |
Support
Questions about the API, your keys or your account? Email admin@lupokits.com. It's the one address for everything Lupokits.