Client Integration Checklist
This checklist explains which communications providers ship with Petunia by default and where customers must bring their own API keys (BYOK), phone numbers, or webhooks. Share it with every new portal so we never mix environments or leak credentials between tenants.
Pricing Model
Regardless of which keys a client uses, they always pay:
- Subscription fee - Monthly/annual platform access
- Commissions - Lead fees, booking fees, closure fees (performance-based)
- Overages - Usage beyond tier allowances
When using platform keys, clients also pay a 1% margin on API costs. BYOK clients avoid this margin but still pay subscription and commissions.
Voice + AI Providers
| Capability | Default Behaviour | BYOK Options | Implementation Status |
|---|---|---|---|
| Cartesia Voice | Petunia supplies a shared platform key. Enabled automatically. | Override via ClientApiKey (provider = 'cartesia'). | ✅ ApiKeyResolver with client priority |
| ElevenLabs Voice | Disabled by default. BYOK only. | ELEVENLABS_API_KEY via encrypted ClientApiKey. | ✅ ApiKeyResolver with client priority |
| Retell Voice | Optional add-on. BYOK only. | RETELL_API_KEY via ClientApiKey. | ✅ ApiKeyResolver with client priority |
| Anthropic AI | Platform key provided. | Override via ClientApiKey (provider = 'anthropic'). | ✅ ApiKeyResolver with client priority |
| OpenAI | Platform key provided. | Override via ClientApiKey (provider = 'openai'). | ✅ ApiKeyResolver with client priority |
Telephony & Messaging Providers
| Provider | Phone Numbers | API Keys | Implementation Status |
|---|---|---|---|
| Twilio | Customer-owned recommended. Platform can provision via API. | Platform fallback + client override via ClientApiKey. | ✅ ApiKeyResolver + Phone Provisioning API |
| SendGrid | N/A (email) | Platform fallback + client override via ClientApiKey. | ✅ ApiKeyResolver with client priority |
| RingCentral | Always customer-owned via OAuth. | Customer OAuth credentials stored in Connection table. | ✅ OAuth connect flow at /api/connections/ringcentral |
Social & Integration Providers
| Provider | OAuth Flow | Implementation Status |
|---|---|---|
| Google Business | Customer connects via OAuth | ✅ /api/connections/google |
| Facebook/Meta | Customer connects via OAuth | ✅ /api/connections/facebook |
| Customer connects via OAuth | ✅ /api/integrations/instagram/connect | |
| Yelp | Customer connects via OAuth | ✅ /api/connections/yelp |
| Customer connects via OAuth | ✅ /api/integrations/linkedin/connect | |
| Twitter/X | Customer connects via OAuth | ✅ /api/integrations/twitter/connect |
| Slack | Customer connects via OAuth | ✅ /api/connections/slack |
| Customer connects via connection setup | ✅ /api/connections/whatsapp | |
| Stripe Connect | Customer connects via OAuth | ✅ /api/integrations/stripe-connect |
Phone Number Provisioning
Petunia can provision phone numbers for customers via Twilio:
API Endpoint: POST /api/twilio/phone-numbers
// Search for available numbers
GET /api/twilio/phone-numbers?action=search&country=US&areaCode=415
// Provision a number
POST /api/twilio/phone-numbers
{
"phoneNumber": "+14155551234",
"portalId": "portal_xxx",
"friendlyName": "Main Line"
}
// List owned numbers
GET /api/twilio/phone-numbers?action=list
// Release a number (platform admin only)
DELETE /api/twilio/phone-numbers?sid=PNxxx
Numbers are automatically mapped to portals via PhoneNumberMapping table.
Key Resolution Flow
1. Request comes in for provider (e.g., 'twilio')
2. ApiKeyResolver.resolveKey('twilio', clientId)
3. Check ClientApiKey table for client-specific key
→ If found: use client key (source: 'client', no margin)
→ If not: fall back to platform env var (source: 'platform', 1% margin)
4. Track usage via PlatformUsageTracker
Where to Configure Keys
| Type | Location | Who Manages | When Used |
|---|---|---|---|
| Platform defaults | .env / Vercel | Petunia team | Demos, fallbacks |
| Client API keys | ClientApiKey table | Client via Admin UI | Production BYOK |
| OAuth credentials | Connection table | System via OAuth flow | Social integrations |
| Phone mappings | PhoneNumberMapping table | Admin via API | Routing calls/SMS |
Billing Integration
Usage is tracked automatically by PlatformUsageTracker:
- SMS: $0.0075/message (Twilio pricing)
- Email: $0.0001/email (SendGrid pricing)
- Voice: $0.05-0.10/minute (varies by provider)
- AI: Token-based (Anthropic/OpenAI pricing)
Billing includes:
- Subscription fees (via Stripe subscription)
- Usage overages (reported as Stripe invoice items)
- Commission fees (leads, bookings, closures)
- 1% API margin on platform key usage
Implementation Checklist
For new portal onboarding:
- Verify subscription tier and set quotas
- Configure voice provider (Cartesia default, or BYOK)
- Set up phone number (provision via API or use existing)
- Connect social accounts (Google, Facebook, Yelp)
- Verify webhook endpoints are receiving events
- Test SMS/Voice flows end-to-end
- Confirm billing is tracking correctly
Yelp Integration Runbook
Overview
- Production tenants ingest Yelp reviews via a durable Prisma-backed queue (
YelpReviewSyncJob) - Demo data (
useDemoService) is restricted to the canonical demo portal (DEMO_COMPANY_ID) - Scheduled Cron route (
/api/cron/yelp-reviews) enqueues jobs for every active connection
Environment Variables
YELP_CLIENT_ID/YELP_CLIENT_SECRET: OAuth credentials for connected Yelp Fusion applicationYELP_API_KEY(legacy): Required for search/lookup helpersCRON_SECRET: Bearer token for Vercel Cron to trigger/api/cron/yelp-reviews
Scheduling
Configure scheduler to invoke GET /api/cron/yelp-reviews every 15 minutes:
{
"crons": [
{ "path": "/api/cron/yelp-reviews", "schedule": "*/15 * * * *" }
]
}
Job Lifecycle
- Jobs in
YelpReviewSyncJobtable with statuses:pending,running,completed,failed,dead_letter - Exponential backoff (base 60s, max 12h) with 5 attempts before
dead_letter - Queue metrics:
GET /api/connections/yelp/jobs
Monitoring Alerts
- Alert when
dead_letter > 0 - Alert when
pendingbacklog consistently > 0 after cron runs - Alert when
processedCount = 0for multiple consecutive runs
Google Business Profile Integration Runbook
Overview
- Production tenants ingest Google reviews via durable Prisma-backed queue (
GoogleReviewSyncJob) - Demo data isolated to canonical demo portal (
DEMO_COMPANY_ID) - Scheduled cron route (
/api/cron/google-reviews) enqueues jobs for eligible connections
Environment Variables
GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET: OAuth credentials from Google Business Profile projectGOOGLE_REDIRECT_URI(optional): Defaults to${NEXT_PUBLIC_SITE_URL}/api/auth/google/callbackCRON_SECRET: Shared bearer token for/api/cron/google-reviews
Scheduling
Configure scheduler to call GET /api/cron/google-reviews every 15 minutes:
{
"crons": [
{ "path": "/api/cron/google-reviews", "schedule": "*/15 * * * *" }
]
}
Job Lifecycle & APIs
- Model:
GoogleReviewSyncJobwith statuses:pending,running,completed,failed,dead_letter - Admin APIs:
GET /api/connections/google/jobs→ queue metricsPOST /api/connections/google/jobs→ enqueue manual syncGET /api/connections/google/jobs/[jobId]→ job status
External References
Last Updated: February 2026