• Skip to main content
  • Skip to navigation
  • Skip to search
    Petunia™
    FeaturesPricingIntegrationsAboutContact
    Log inStart free trialSign up
    Loading
    Petunia™

    Reimagining customer communication for the modern business.

    Product

    • Features
    • Pricing
    • Integrations
    • Roadmap
    • What's New

    Resources

    • Help Center
    • Documentation
    • Guides
    • API Reference
    • Community
    • Support

    Company

    • About Us
    • Careers
    • Blog
    • Press
    • Contact

    © 2026 Gray Group International LLC. All rights reserved.·
    Made by gardenpatch 🌱

    Privacy PolicyTerms of ServiceCookie Policy

    Petunia™ is a trademark of Gray Group International LLC. The Petunia name, brand, product design, and content are proprietary. Unauthorized use, imitation, or copying is prohibited.

    Documentation

    PLATFORM_INTEGRATIONS

    docs/runbooks/PLATFORM_INTEGRATIONS.md
    Docs homeGuidesSupport
    Quick links
    Start here
    How the docs are organized.
    Environment setup
    Configure env + run locally.
    Unified Inbox
    Inbox concepts & behavior.
    Voice AI setup
    Providers, Twilio, testing.
    Pricing model
    Source-of-truth pricing.
    Operations runbook
    How to operate safely.

    Platform Integrations Runbook

    This document covers webhook configuration, environment variables, and operational procedures for all external platform integrations in Petunia.

    Owner: Riley Chen
    Version: 1.0.1
    Last Updated: 2025-12-05

    Verification

    • Last verified: 2025-12-05 (manual-local-2025-12-05-integrations; GH Actions run ID pending)
    • CI jobs: ci.yml webhook + API stages (replace with GH Actions run ID once available)
    • Commands (demo portal + sandbox keys only):
      • pnpm test:api --config jest.config.api.cjs --runTestsByPath tests/api/webhooks/yelp/messages.test.ts tests/api/webhooks/google/notifications.test.ts tests/app/api/webhooks/facebook/facebook-signature.test.ts tests/app/api/webhooks/instagram/instagram-signature.test.ts
      • pnpm test:api --config jest.config.api.cjs --runTestsByPath tests/api/communications/voice/status/callId.test.ts tests/api/communications/voice/elevenlabs/call.test.ts
      • pnpm cypress:run --spec "cypress/e2e/inbox/inbox-demo-production.cy.ts"
      • pnpm db:health
    • Environment: CI/staging demo portal seeded; all external providers pointed to sandbox/test accounts; no production client data
    • Next verification due: 2026-01-04 (attach GH Actions run link + updated coverage)

    Table of Contents

    1. WhatsApp Business
    2. Twilio SMS/MMS
    3. SendGrid Email
    4. Facebook Messenger
    5. Instagram DM
    6. Yelp
    7. Google Business
    8. RingCentral SMS

    WhatsApp Business

    Overview

    WhatsApp Business Cloud API integration for receiving and sending WhatsApp messages through the Unified Inbox.

    Webhook Endpoint

    POST /api/webhooks/whatsapp
    GET  /api/webhooks/whatsapp  (verification challenge)
    

    Environment Variables

    VariableRequiredDescription
    WHATSAPP_APP_SECRETProductionMeta App Secret for signature validation
    WHATSAPP_VERIFY_TOKENYesCustom token for webhook verification
    WHATSAPP_ACCESS_TOKENYesGraph API access token for sending messages
    WHATSAPP_PHONE_NUMBER_IDYesWhatsApp Business phone number ID

    Meta Business App Setup

    1. Create a Meta Business App at developers.facebook.com
    2. Add WhatsApp product to your app
    3. Configure webhook URL: https://your-domain.com/api/webhooks/whatsapp
    4. Set verify token to match WHATSAPP_VERIFY_TOKEN
    5. Subscribe to events: messages, message_echoes

    Database Models

    • WhatsAppConnection - Stores portal-to-phone-number mappings
    • PhoneNumberMapping - Fallback phone number resolution
    • Message - Stores inbound/outbound messages
    • Conversation - Groups messages by sender

    Supported Message Types

    TypeInboundOutbound
    Text✅✅
    Image✅ (media ID stored)✅
    Document✅ (media ID stored)✅
    Audio✅ (media ID stored)✅
    Video✅ (media ID stored)✅
    Location✅❌
    Sticker✅❌

    Status Updates

    The webhook processes delivery status updates:

    • sent - Message accepted by WhatsApp
    • delivered - Message delivered to recipient
    • read - Message read by recipient
    • failed - Message delivery failed (error details stored)

    Troubleshooting

    SymptomLikely CauseAction
    403 on webhook verificationWrong verify tokenCheck WHATSAPP_VERIFY_TOKEN matches Meta config
    401 on POSTInvalid signatureVerify WHATSAPP_APP_SECRET is correct
    Messages not appearingNo portal mappedCheck WhatsAppConnection has matching phoneNumberId
    Duplicate messagesRetry from MetaSystem deduplicates via externalId - safe to ignore

    Twilio SMS/MMS

    Overview

    Twilio integration for inbound SMS/MMS with optional autoresponder.

    Webhook Endpoint

    POST /api/webhooks/twilio         (inbound messages)
    POST /api/webhooks/twilio/status  (delivery status)
    

    Environment Variables

    VariableRequiredDescription
    TWILIO_ACCOUNT_SIDYesTwilio account identifier
    TWILIO_AUTH_TOKENProductionAuth token for signature validation
    TWILIO_WEBHOOK_URLOptionalOverride URL for signature validation
    TWILIO_PHONE_NUMBERYesYour Twilio phone number

    Twilio Console Setup

    1. Go to Phone Numbers → Manage → Active Numbers
    2. Select your number
    3. Under Messaging, set webhook URL: https://your-domain.com/api/webhooks/twilio
    4. Method: HTTP POST
    5. For status callbacks, configure: https://your-domain.com/api/webhooks/twilio/status

    Message Flow

    1. Inbound SMS received at Twilio
    2. Twilio POSTs to webhook with signature
    3. Webhook validates signature (production) or skips (development)
    4. Message stored in Message table with channel: 'sms'
    5. Conversation created/updated via conversationService
    6. AutoResponder generates TwiML response (if configured)

    TwiML Response Format

    <?xml version="1.0" encoding="UTF-8"?>
    <Response>
      <Message>Your auto-reply here</Message>
    </Response>
    

    Troubleshooting

    SymptomLikely CauseAction
    401 responseInvalid signatureCheck TWILIO_AUTH_TOKEN, verify webhook URL
    No portal foundMissing phone mappingAdd entry to PhoneNumberMapping
    Auto-reply not sentAutoResponder disabledCheck business settings
    MMS attachments missingMedia URLs not storedCheck metadata.mediaUrls field

    SendGrid Email

    Overview

    SendGrid integration for inbound emails (Inbound Parse) and delivery tracking (Event Webhook).

    Webhook Endpoint

    POST /api/webhooks/sendgrid  (inbound emails & events)
    GET  /api/webhooks/sendgrid  (health check)
    

    Environment Variables

    VariableRequiredDescription
    SENDGRID_API_KEYYesAPI key for sending emails
    SENDGRID_EVENT_WEBHOOK_SECRETProductionSigning secret for event webhook
    SENDGRID_INBOUND_WEBHOOK_SECRETOptionalAdditional inbound security

    SendGrid Configuration

    Inbound Parse Setup:

    1. Go to Settings → Inbound Parse
    2. Add Host & URL: https://your-domain.com/api/webhooks/sendgrid
    3. Check "POST the raw, full MIME message"
    4. Add MX records for your domain pointing to mx.sendgrid.net

    Event Webhook Setup:

    1. Go to Settings → Mail Settings → Event Webhook
    2. HTTP Post URL: https://your-domain.com/api/webhooks/sendgrid
    3. Select events: Delivered, Bounced, Opened, Clicked, Spam Report, Unsubscribe

    Inbound Email Processing

    • Portal matched by recipient email domain
    • Contact created/found by sender email
    • Conversation created with subject as title
    • Message stored via messagePersistence.storeInbound()

    Event Types Processed

    EventAction
    deliveredUpdate message status to delivered
    bounceMark as failed, store bounce reason
    openTrack email opens
    clickTrack link clicks
    spamreportFlag for compliance
    unsubscribeUpdate contact preferences

    Troubleshooting

    SymptomLikely CauseAction
    Inbound emails not receivedMX records misconfiguredVerify DNS settings
    No portal foundEmail domain not configuredCheck Business/Location email settings
    Events not processingInvalid signatureVerify SENDGRID_EVENT_WEBHOOK_SECRET

    Facebook Messenger

    Overview

    Facebook Messenger integration for business page messaging. Creates portal-scoped Conversations for unified inbox integration.

    Webhook Endpoint

    POST /api/webhooks/facebook
    GET  /api/webhooks/facebook  (verification)
    

    Environment Variables

    VariableRequiredDescription
    FACEBOOK_CLIENT_SECRETProductionApp secret for signature validation
    FACEBOOK_WEBHOOK_VERIFY_TOKENYesWebhook verification token
    FACEBOOK_PAGE_ACCESS_TOKENYesPage access token for API calls

    Facebook App Setup

    1. Create app at developers.facebook.com
    2. Add Messenger product
    3. Generate page access token
    4. Configure webhook: https://your-domain.com/api/webhooks/facebook
    5. Subscribe to: messages, messaging_postbacks

    Database Models

    • FacebookConnection - Links pageId to portal via ConnectionIntegration → Client → Business
    • Message - Stores inbound messages with portalId, conversationId
    • Conversation - Groups messages by sender
    • Contact - Created/found per sender

    Message Flow

    1. Webhook receives message/comment from Facebook
    2. Portal resolved via FacebookConnection.pageId
    3. Contact created/found by sender ID
    4. Conversation created via conversationService.findOrCreateFromChannel()
    5. Message stored with full portal and conversation linking
    6. Conversation aggregates updated

    Event Types Handled

    EventAction
    messageStore as incoming DM, create/update conversation
    postbackLog button click payload
    readLog read receipt
    deliveryLog delivery confirmation
    commentsStore as comment message with separate conversation
    postsLog new posts
    ratingsLog page ratings

    Troubleshooting

    SymptomLikely CauseAction
    403 on verificationWrong verify tokenCheck FACEBOOK_WEBHOOK_VERIFY_TOKEN matches app config
    401 on POSTInvalid signatureVerify FACEBOOK_CLIENT_SECRET is correct
    Messages not appearingNo portal mappedCheck FacebookConnection has matching pageId
    Duplicate messagesRetry from MetaSystem deduplicates via externalId (message.mid)

    Instagram DM

    Overview

    Instagram Direct Messages, comments, and mentions integration. Uses Facebook Graph API and creates portal-scoped Conversations for unified inbox integration.

    Webhook Endpoint

    POST /api/webhooks/instagram
    GET  /api/webhooks/instagram  (verification)
    

    Environment Variables

    VariableRequiredDescription
    FACEBOOK_CLIENT_SECRETProductionApp secret for signature validation (same as Facebook)
    FACEBOOK_WEBHOOK_VERIFY_TOKENYesWebhook verification token (same as Facebook)
    INSTAGRAM_ACCESS_TOKENYesInstagram Business account token

    Requirements

    • Instagram Business or Creator account
    • Facebook Page linked to Instagram account
    • Instagram Graph API access

    Database Models

    • FacebookConnection - Instagram uses Facebook's connection model (pageId = Instagram Business Account ID)
    • Message - Stores DMs, comments, mentions with portalId, conversationId
    • Conversation - Separate conversations for DMs vs comments vs mentions
    • Contact - Created per unique sender

    Message Flow

    1. Webhook receives Instagram event (DM, comment, or mention)
    2. Portal resolved via FacebookConnection (Instagram uses Facebook's Graph API)
    3. Contact created/found by sender ID
    4. Conversation created with type-specific externalId pattern:
      • DMs: sender's Instagram ID
      • Comments: ig-comment-{senderId}
      • Mentions: ig-mention-{senderId}
    5. Message stored with metadata including platform, type, username
    6. Conversation aggregates updated

    Event Types Handled

    EventAction
    messagingStore as Instagram DM
    commentsStore as comment with post ID reference
    mentionsStore as mention with media/comment ID

    Troubleshooting

    SymptomLikely CauseAction
    403 on verificationWrong verify tokenCheck FACEBOOK_WEBHOOK_VERIFY_TOKEN
    No portal foundInstagram not linked to FacebookConnectionCheck connection configuration
    Comments not appearingWrong Instagram Business Account IDVerify pageId in FacebookConnection

    Yelp

    Overview

    See INTEGRATIONS_YELP.md for comprehensive Yelp documentation.

    Webhook Endpoint

    POST /api/webhooks/yelp  (incoming messages)
    

    Key Environment Variables

    VariableRequiredDescription
    YELP_CLIENT_IDYesOAuth client ID
    YELP_CLIENT_SECRETYesOAuth client secret
    YELP_API_KEYYesAPI key for Fusion API

    Google Business

    Overview

    Google Business Profile integration for reviews, questions, and answers via Pub/Sub. Creates portal-scoped Conversations for unified inbox integration.

    Webhook Endpoint

    POST /api/webhooks/google  (Pub/Sub push endpoint)
    

    Environment Variables

    VariableRequiredDescription
    GOOGLE_CLIENT_IDYesOAuth 2.0 client ID
    GOOGLE_CLIENT_SECRETYesOAuth 2.0 client secret
    GOOGLE_REDIRECT_URIYesOAuth callback URL
    GOOGLE_PUBSUB_AUDIENCEOptionalExpected JWT audience for Pub/Sub auth

    Database Models

    • GoogleConnection - Links businessId (locationName) to portal via ConnectionIntegration → Client → Business
    • Message - Stores reviews/questions/answers with portalId, conversationId
    • Conversation - Groups interactions by reviewer/author name
    • Contact - Created per unique reviewer/author

    Message Flow

    1. Google Cloud Pub/Sub pushes notification to webhook
    2. Webhook verifies JWT auth token (production)
    3. Decodes base64 message payload
    4. Portal resolved via GoogleConnection.businessId (locationName)
    5. Contact created/found by reviewer/author display name
    6. Conversation created with type-specific externalId pattern:
      • Reviews: google-review-{reviewerName}
      • Q&A: google-qa-{authorName}
    7. Message stored with metadata including starRating, type, questionId
    8. Conversation aggregates updated

    Event Types Handled

    EventAction
    NEW_REVIEWStore review with star rating
    UPDATED_REVIEWStore review update (skip business replies)
    NEW_QUESTIONStore Q&A question
    NEW_ANSWERStore Q&A answer (customer/local guide only)

    Troubleshooting

    SymptomLikely CauseAction
    401 on POSTInvalid Pub/Sub JWTCheck GOOGLE_PUBSUB_AUDIENCE matches
    No portal foundLocationName not matchedVerify GoogleConnection.businessId
    Reviews not appearingWrong location name formatCheck Pub/Sub message payload
    Duplicate reviewsRetry from Pub/SubSystem deduplicates via reviewId

    RingCentral SMS

    Overview

    RingCentral integration for inbound SMS with conversation creation for unified inbox integration.

    Webhook Endpoint

    POST /api/webhooks/ringcentral  (inbound SMS and status updates)
    GET  /api/webhooks/ringcentral  (health check)
    

    Environment Variables

    VariableRequiredDescription
    RINGCENTRAL_CLIENT_IDYesOAuth client ID
    RINGCENTRAL_CLIENT_SECRETYesOAuth client secret
    RINGCENTRAL_SERVERYesAPI server URL
    RINGCENTRAL_WEBHOOK_SECRETProductionWebhook validation secret

    Database Models

    • PhoneNumberMapping - Links phone numbers to portals
    • PhoneNumber (fallback) - Business phone number records
    • Message - Stores inbound SMS with portalId, conversationId
    • Conversation - Groups messages by sender phone
    • Contact - Created per unique sender phone

    Message Flow

    1. RingCentral sends webhook with Validation-Token header for subscription setup
    2. Webhook validates signature (production)
    3. Portal resolved via PhoneNumberMapping or PhoneNumber tables
    4. Contact created/found by sender phone number
    5. Conversation created via conversationService.findOrCreateFromChannel()
    6. Message stored with full portal and conversation linking
    7. Conversation aggregates updated
    8. Status updates processed via messagePersistence.updateStatusByProviderMessageId()

    Event Types Handled

    EventAction
    Validation requestReturn Validation-Token header for subscription
    Inbound SMSStore message, create/update conversation
    Status updateUpdate message delivery status

    Troubleshooting

    SymptomLikely CauseAction
    Subscription failingValidation-Token not returnedCheck webhook returns header correctly
    401 on POSTInvalid signatureVerify RINGCENTRAL_WEBHOOK_SECRET
    No portal foundPhone number not mappedAdd to PhoneNumberMapping
    Duplicate messagesRetry from RingCentralSystem deduplicates via providerMessageId

    Common Patterns

    Portal Resolution

    All webhooks resolve the target portal using:

    1. Platform-specific connection table (e.g., WhatsAppConnection)
    2. Fallback to PhoneNumberMapping for phone-based channels
    3. Email domain matching for email channels

    Conversation Creation

    All inbound messages use conversationService.findOrCreateFromChannel():

    const conversation = await conversationService.findOrCreateFromChannel({
      portalId: portal.id,
      channel: 'sms' | 'email' | 'whatsapp' | etc,
      externalId: senderIdentifier,  // Phone, email, or platform ID
      title: conversationTitle,
      participant: {
        contactId,
        name,
        phone?,
        email?,
      },
    });
    

    Message Storage

    All channels store messages with consistent provider tracking:

    await prisma.message.create({
      data: {
        id: nanoid(),
        externalId: platformMessageId,
        content,
        channel,
        contactId,
        portalId,
        conversationId,
        direction: 'incoming',
        status: 'received',
        providerId: platformName,
        providerMsgId: platformMessageId,
        providerStatus: 'received',
      },
    });
    

    Signature Validation

    Production environments validate webhook signatures:

    • WhatsApp: HMAC-SHA256 with x-hub-signature-256 header
    • Twilio: HMAC-SHA1 with x-twilio-signature header
    • SendGrid: ECDSA signature validation
    • Development environments skip validation for easier testing

    Monitoring & Alerts

    Health Checks

    All webhooks support health check via GET:

    curl https://your-domain.com/api/webhooks/{platform}
    # Returns: { "status": "ok", "webhook": "{platform}", "supports": [...] }
    

    Key Metrics to Monitor

    • Webhook response times (p95 < 500ms)
    • Error rates by platform
    • Message processing latency
    • Dead letter queue sizes (if applicable)
    • Signature validation failures

    Recommended Alerts

    1. Signature failures > 5/min - Possible secret rotation needed
    2. No messages in 1 hour - Webhook may be misconfigured
    3. Error rate > 5% - Platform API issues or code bugs
    4. p95 latency > 2s - Performance degradation

    Testing

    Local Development

    Use ngrok or similar for local webhook testing:

    ngrok http 3000
    # Configure platform with ngrok URL
    

    Test Endpoints

    # Health check
    curl http://localhost:3000/api/webhooks/whatsapp
    
    # Simulate inbound (dev only, no signature)
    curl -X POST http://localhost:3000/api/webhooks/twilio \
      -d "From=+1234567890&To=+0987654321&Body=Test&MessageSid=SM123"
    

    Integration Tests

    pnpm test -- tests/app/api/webhooks/
    
    On this page
    VerificationTable of ContentsWhatsApp BusinessOverviewWebhook EndpointEnvironment VariablesMeta Business App SetupDatabase ModelsSupported Message TypesStatus UpdatesTroubleshootingTwilio SMS/MMSOverviewWebhook EndpointEnvironment VariablesTwilio Console SetupMessage FlowTwiML Response FormatTroubleshootingSendGrid EmailOverviewWebhook EndpointEnvironment VariablesSendGrid ConfigurationInbound Email ProcessingEvent Types ProcessedTroubleshootingFacebook MessengerOverviewWebhook EndpointEnvironment VariablesFacebook App Setup