Overview

Webhooks provide real-time notifications when events occur in your OpenPhone workspace, enabling powerful integrations and automated workflows. Configure webhooks to send event data to your applications instantly when calls complete, messages arrive, or contacts change.

How webhooks work

Event-driven notifications: Webhooks deliver instant notifications when specific events occur in your OpenPhone workspace. When an event triggers, OpenPhone sends an HTTP POST request to your specified URL with detailed event data. Typical workflow:
  1. Configure webhook URL and select event types
  2. OpenPhone monitors for specified events
  3. When event occurs, OpenPhone sends POST request with event payload
  4. Your application processes the event data and responds
  5. OpenPhone logs the delivery status and retries if needed
Webhook configuration requires workspace Owner or Admin permissions. Settings are managed through web and desktop apps only.
Webhook configuration interface:
Webhook creation form in OpenPhone settings

Available webhook events

Messaging events

Text message notifications:
  • message.received: Text message received by workspace phone number (includes media attachments)
  • message.delivered: Text message sent from workspace and successfully delivered (includes media)
AI-powered insights:
  • call.summary.completed: AI-generated call summary available in event payload
  • call.transcript.completed: Complete call transcript available in event payload

Voice events

Call status notifications:
  • call.ringing: Incoming call being received by workspace phone number
  • call.completed: Call finished (answered or unanswered, may include voicemail)
  • call.recording.completed: Call recording available at provided URL

Contact events

Contact management:
  • contact.updated: Contact created or modified in workspace
  • contact.deleted: Contact removed from workspace

Setting up webhooks

Configuration requirements

Required parameters:
ParameterRequirementDescription
URLRequiredWebhook handler endpoint (HTTPS strongly recommended for production)
Event typesRequiredSelect one or more event types to monitor
ResourcesRequiredChoose phone numbers (calls/messages) or users/groups (contacts) to monitor
Optional parameters:
ParameterDescription
LabelDescriptive name for webhook identification and management

Setup process

To create a webhook:
  1. Navigate to SettingsWebhooks in OpenPhone
  2. Click Create webhook
  3. Enter your webhook handler URL
  4. Select event types to monitor
  5. Choose phone numbers or contact resources
  6. Add optional label for identification
  7. Save and test configuration

Building webhook handlers

Handler requirements

Technical specifications:
  • Accept HTTP POST requests at your webhook URL
  • Process JSON event payload in request body
  • Respond with 2xx HTTP status code within 10 seconds
  • Verify webhook signature for security
  • Handle retries and failures gracefully
Response handling:
  • Success: Return 2xx status code (no response body required)
  • Failure: Non-2xx response triggers OpenPhone retry sequence
  • Timeout: No response within 10 seconds initiates retries
Development flexibility: Webhook handlers can be built in any programming language that supports HTTP requests and responses. Deploy to cloud platforms, servers, or serverless functions.

Security and authentication

Webhook signature verification: All webhook calls include cryptographic signatures to verify authenticity and prevent spoofing attacks. Signature header format:
'openphone-signature': 'hmac;1;1639710054089;mw1K4fvh5m9XzsGon4C5N3KvL0bkmPZSAy
b/9Vms2Qo='
Signature structure:
<scheme>;<version>;<timestamp>;<signature>
ComponentDescriptionCurrent Value
schemeSignature algorithmAlways “hmac”
versionSignature versionAlways “1”
timestampSignature generation timeUnix timestamp
signatureBase64 encoded HMAC signatureSHA256 digest

Signature verification process

Verification steps:
  1. Extract components from openphone-signature header
  2. Prepare signed data by concatenating timestamp + "." + payload
  3. Decode signing key from base64 (available in webhook details)
  4. Compute HMAC-SHA256 using decoded key and signed data
  5. Compare result with signature from header
Important requirements:
  • Remove all whitespace and newlines from JSON payload before concatenation
  • Use binary form of base64-decoded signing key for HMAC computation
  • Ensure exact string matching for verification success
Getting your signing key:
  1. Go to webhook details page in OpenPhone
  2. Click ellipses (⋯) at top right
  3. Select “Reveal signing secret”
  4. Copy base64-encoded key for your application

Implementation examples

Node.js webhook handler:
const express = require("express")
const bodyParser = require('body-parser')
const crypto = require('node:crypto');

const app = express()
const port = 8000

app.use(bodyParser.json())

app.post("/", function (req, res) {
  // signingKey is from "Reveal Signing Secret" in the OpenPhone app.
  const signingKey = 'R2ZLM2o0bFhBNVpyUnU2NG9mYXQ1MHNyR3pvSUhIVVg='

  // Parse the fields from the openphone-signature header.
  const signature = req.headers['openphone-signature']
  const fields = signature.split(';')
  const timestamp = fields[2]
  const providedDigest = fields[3]

  // Compute the data covered by the signature.
  const signedData = timestamp + '.' + JSON.stringify(req.body)

  // Convert the base64-encoded signing key to binary.
  const signingKeyBinary = Buffer.from(signingKey, 'base64').toString('binary')

  // Compute the SHA256 HMAC digest.
  const computedDigest = crypto.createHmac('sha256',signingKeyBinary)
    .update(Buffer.from(signedData,'utf8'))
    .digest('base64')

  // Verify signature matches
  if (providedDigest === computedDigest) {
    console.log(`signature verification succeeded`)
    // Process webhook event here
  } else {
    console.log(`signature verification failed`)
    return res.status(401).send('Unauthorized')
  }

  res.send({})
});

app.listen(port, function () {
  console.log(`webhook server listening on port ${port}`)
})
Python webhook handler:
import base64
import hmac
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/', methods=['POST'])
def handle_webhook_call():
    # signingKey is from "Reveal Signing Secret" in the OpenPhone app.
    signing_key = 'R2ZLM2o0bFhBNVpyUnU2NG9mYXQ1MHNyR3pvSUhIVVg='

    # Parse the fields from the openphone-signature header.
    signature = request.headers['openphone-signature']
    fields = signature.split(';')
    timestamp = fields[2]
    provided_digest = fields[3]

    # Compute the data covered by the signature as bytes.
    signed_data_bytes = b''.join([timestamp.encode(), b'.', request.data])

    # Convert the base64-encoded signing key to bytes.
    signing_key_bytes = base64.b64decode(signing_key)

    # Compute the SHA256 HMAC digest.
    hmac_object = hmac.new(signing_key_bytes, signed_data_bytes, 'sha256')
    computed_digest = base64.b64encode(hmac_object.digest()).decode()

    # Verify signature matches
    if provided_digest == computed_digest:
        print('signature verification succeeded')
        # Process webhook event here
    else:
        print('signature verification failed')
        return jsonify({'error': 'Unauthorized'}), 401
        
    return jsonify({})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=True)
Future versions may include multiple signatures separated by commas. Split the header value on commas to handle multiple signatures if needed.

Security best practices

Replay attack protection: Implement timestamp validation to prevent replay attacks:
  • Compare signature timestamp with current time
  • Reject requests with timestamps outside acceptable tolerance (e.g., 5 minutes)
  • Each webhook call generates unique timestamp and signature
  • Retries automatically include new timestamps
Additional security measures:
  • Always use HTTPS URLs for production webhooks
  • Store signing keys securely (environment variables, secret management)
  • Implement proper error handling and logging
  • Consider rate limiting for webhook endpoints

Error handling and retries

Automatic retry system

Retry behavior:
  • Trigger conditions: Non-2xx response codes or 10-second timeout
  • Backoff strategy: Exponential backoff with increasing delays
  • Retry duration: Up to 3 days of retry attempts
  • Final failure: Email notification sent to webhook creator
Retry timeline:
  • Initial retries happen quickly for minimal delay
  • Delays increase exponentially with each attempt
  • Prioritizes delivery close to original event time
  • Automatic status tracking throughout process

Manual retry options

Webhook management:
  • View delivery status in OpenPhone webhook details
  • Manually retry failed webhook calls anytime
  • Failed webhooks marked with ‘failure’ status
  • Successful manual retries update status to ‘success’

Failure notifications

When retries exhaust:
  • Email alert sent to webhook creator
  • Webhook call marked as permanently failed
  • Event details preserved for manual review
  • Option to retry manually when issues resolved

Testing and validation

Development testing

Local testing options:
  • HTTP clients: Use cURL, Postman, or Insomnia to send test POST requests
  • Local URLs: Test against localhost during development
  • Mock payloads: Create sample event JSON matching OpenPhone format
  • Signature testing: Verify HMAC validation logic with test keys

OpenPhone test features

Built-in test request:
  1. Navigate to webhook details page in OpenPhone
  2. Click ellipses (⋯) at top right
  3. Select “Send Test Request”
  4. OpenPhone sends sample event to your webhook URL
  5. Verify signature verification and response handling
Test requests require publicly accessible webhook URLs. Local development URLs won’t work with OpenPhone’s test feature.

Live event testing

Real event triggers:
  • Configure webhook for specific event type (e.g., message.received)
  • Select your OpenPhone number in webhook resources
  • Trigger actual event (send text to your number)
  • Monitor webhook delivery and processing
  • Verify complete end-to-end functionality
Testing checklist:
  • Webhook receives POST requests correctly
  • Signature verification works
  • Event payload parsing succeeds
  • Application logic processes events properly
  • Error responses trigger retries
  • Success responses stop retry sequence

Troubleshooting

Common issues and solutions

Configuration problems:
  • Verify webhook URL is correct and accessible
  • Check webhook status is enabled in settings
  • Confirm event types are properly selected
  • Validate resources (phone numbers/users/groups) are correct
Response issues:
  • HTTP status codes: Ensure 2xx responses for successful processing
  • Response timing: Return responses within 10-second timeout
  • Error handling: Implement proper error responses for debugging
Security verification:
  • Signature validation: Verify HMAC signature computation
  • Key format: Ensure signing key is properly base64 decoded
  • Timestamp handling: Check timestamp extraction and concatenation

Debugging tools

Event monitoring:
  • Events log: View delivery history in webhook details page
  • Status tracking: Monitor success/failure rates
  • Retry attempts: Review automatic retry sequences
  • Manual testing: Use “Send Test Request” for immediate validation
Performance optimization:
  • Response time: Keep processing under 10 seconds
  • Error rates: Minimize failures to reduce retry overhead
  • Logging: Implement comprehensive request/response logging
  • Monitoring: Set up alerts for webhook delivery failures

Event payload examples

Message events

message.received payload:
{  
  "id": "EVc67ec998b35c41d388af50799aeeba3e",  
  "object": "event",  
  "apiVersion": "v2",  
  "createdAt": "2022-01-23T16:55:52.557Z",  
  "type": "message.received",  
  "data": {  
    "object": {  
      "id": "AC24a8b8321c4f4cf2be110f4250793d51",  
      "object": "message",  
      "from": "+14155550100",  
      "to": "+13105550199",  
      "direction": "incoming",  
      "body": "Hello",  
      "media": [  
        {  
         "url": "https://storage.googleapis.com/opstatics-dev/6c908000ada94d9fb206649ecb8cc928",  
         "type": "image/jpeg"  
        }  
      ],  
      "status": "received",  
      "createdAt": "2022-01-23T16:55:52.420Z",  
      "userId": "USu5AsEHuQ",  
      "phoneNumberId": "PNtoDbDhuz",  
      "conversationId": "CN78ba0373683c48fd8fd96bc836c51f79"  
    }  
  }  
}
message.delivered payload:
{  
  "id": "EVdefd85c2c3b740429cf28ade5b69bcba",  
  "object": "event",  
  "apiVersion": "v2",  
  "createdAt": "2022-01-23T17:05:56.220Z",  
  "type": "message.delivered",  
  "data": {  
    "object": {  
      "id": "ACcdcc2668c4134c3cbfdacb9e273cac6f",  
      "object": "message",  
      "from": "+13105550199",  
      "to": "+14155550100",  
      "direction": "outgoing",  
      "body": "Have a nice day",  
      "media": [  
        {  
         "url": "https://opstatics-dev.s3.amazonaws.com/i/ab6084db-5259-42c0-93c1-e17fb2628567.jpeg",  
         "type": "image/jpeg"  
        }  
      ],  
      "status": "delivered",  
      "createdAt": "2022-01-23T17:05:45.195Z",  
      "userId": "USu5AsEHuQ",  
      "phoneNumberId": "PNtoDbDhuz",  
      "conversationId": "CN78ba0373683c48fd8fd96bc836c51f79"  
    }  
  }  
}

Call events

call.ringing payload:
{  
  "id": "EV95c3708f9112412a834cc8d415470cd8",  
  "object": "event",  
  "apiVersion": "v2",  
  "createdAt": "2022-01-23T17:07:51.454Z",  
  "type": "call.ringing",  
  "data": {  
    "object": {  
      "id": "ACbaee66e137f0467dbed5ad4bc8d60800",  
      "object": "call",  
      "from": "+14155550100",  
      "to": "+13105550199",  
      "direction": "incoming",  
      "media": [],  
      "voicemail": null,  
      "status": "ringing",  
      "createdAt": "2022-01-23T17:07:51.116Z",  
      "answeredAt": null,  
      "completedAt": null,  
      "userId": "USu5AsEHuQ",  
      "phoneNumberId": "PNtoDbDhuz",  
      "conversationId": "CN78ba0373683c48fd8fd96bc836c51f79"  
    }  
  }  
}
call.completed (incoming with voicemail):
{  
  "id": "EVd39d3c8d6f244d21a9131de4fc9350d0",  
  "object": "event",  
  "apiVersion": "v2",  
  "createdAt": "2022-01-24T19:22:25.427Z",  
  "type": "call.completed",  
  "data": {  
    "object": {  
      "id": "ACa29ee906a4e04312a6928427b1c21721",  
      "object": "call",  
      "from": "+14145550100",  
      "to": "+13105550199",  
      "direction": "incoming",  
      "media": [],  
      "voicemail": {  
        "url": "https://m.openph.one/static/85ad4740be6048e4a80efb268d347482.mp3",  
        "type": "audio/mpeg",  
        "duration": 7  
      },  
      "status": "completed",  
      "createdAt": "2022-01-24T19:21:59.545Z",  
      "answeredAt": null,  
      "completedAt": "2022-01-24T19:22:19.000Z",  
      "userId": "USu5AsEHuQ",  
      "phoneNumberId": "PNtoDbDhuz",  
      "conversationId": "CN78ba0373683c48fd8fd96bc836c51f79"  
    }  
  }  
}
call.completed (outgoing answered call):
{  
  "id": "EV348de11e4b134fa48017ac45a251dd3e",  
  "object": "event",  
  "apiVersion": "v2",  
  "createdAt": "2022-01-24T19:28:45.370Z",  
  "type": "call.completed",  
  "data": {  
    "object": {  
      "id": "AC7ab6f57e62924294925d0ea961de7dc5",  
      "object": "call",  
      "from": "+13105550199",  
      "to": "+14155550100",  
      "direction": "outgoing",  
      "media": [],  
      "voicemail": null,  
      "status": "completed",  
      "createdAt": "2022-01-24T19:28:33.892Z",  
      "answeredAt": "2022-01-24T19:28:42.000Z",  
      "completedAt": "2022-01-24T19:28:45.000Z",  
      "userId": "USu5AsEHuQ",  
      "phoneNumberId": "PNtoDbDhuz",  
      "conversationId": "CN78ba0373683c48fd8fd96bc836c51f79"  
    }  
  }  
}
call.recording.completed payload:
{  
  "id": "EVda6e196255814311aaac1983005fa2d9",  
  "object": "event",  
  "apiVersion": "v2",  
  "createdAt": "2022-01-24T19:30:55.400Z",  
  "type": "call.recording.completed",  
  "data": {  
    "object": {  
      "id": "AC0d3b9011efa041d78c864019ad9e948c",  
      "object": "call",  
      "from": "+14155550100",  
      "to": "+13105550199",  
      "direction": "incoming",  
      "media": [  
        {   
          "url": "https://storage.googleapis.com/opstatics-dev/b5f839bc72a24b33a7fc032f78777146.mp3",  
          "type": "audio/mpeg",  
          "duration": 7  
        }  
      ],  
      "voicemail": null,  
      "status": "completed",  
      "createdAt": "2022-01-24T19:30:34.675Z",  
      "answeredAt": "2022-01-24T19:30:38.000Z",  
      "completedAt": "2022-01-24T19:30:48.000Z",  
      "userId": "USu5AsEHuQ",  
      "phoneNumberId": "PNtoDbDhuz",  
      "conversationId": "CN78ba0373683c48fd8fd96bc836c51f79"  
    }  
  }  
}

Contact events

contact.updated and contact.deleted payload:
{  
  "id": "EVe844e47e9fa4494d9acfa1144839ed94",  
  "object": "event",  
  "createdAt": "2022-01-24T19:44:09.579Z",  
  "apiVersion": "v2",  
  "type": "contact.updated",  
  "data": {  
    "object": {  
      "id": "CT61eeff33f3b14cfe6358cb52",  
      "object": "contact",  
      "firstName": "Jane",  
      "lastName": "Smith",  
      "company": "Comp Inc",  
      "role": "Agent",  
      "pictureUrl": null,  
      "fields": [  
         {  
           "name": "Phone",  
           "type": "phone-number",  
           "value": "+14155551212"  
         },  
         {  
           "name": "Email",  
           "type": "email",  
           "value": null  
         },  
         {  
           "name": "Prop1",  
           "type": "string",  
           "value": "Value12"  
         }  
      ],  
      "notes": [  
        {  
          "text": "@USu5AsEHuQ mynote 😂",  
          "enrichment": {  
            "taggedIds": {   
              "groupIds": [],   
              "userIds": [  
                "USu5AsEHuQ"  
              ],  
              "orgIds": []  
            },  
            "tokens": {   
              "USu5AsEHuQ": {   
                "token": "USu5AsEHuQ",   
                "replacement": "Tom Smith",   
                "type": "mention",   
                "locations": [  
                  {   
                    "startIndex": 1,   
                    "endIndex": 11   
                  }  
                ]   
              }   
            }   
          },  
          "createdAt": "2022-01-24T19:35:38.323Z",  
          "updatedAt": "2022-01-24T19:35:38.323Z",  
          "userId": "USu5AsEHuQ"  
        }  
      ],  
      "sharedWith": [  
        "USu5AsEHuQ"  
       ],  
      "createdAt": "2022-01-24T19:35:38.318Z",  
      "updatedAt": "2022-01-24T19:44:09.565Z",  
      "userId": "USu5AsEHuQ"  
    }  
  }  
}

AI analysis events

call.summary.completed payload:
{  
  "id": "EVc86d16fed5314cf6bd7bf13f11c65fd2",  
  "object": "event",  
  "apiVersion": "v3",  
  "createdAt": "2024-09-05T15:30:24.213Z",  
  "type": "call.summary.completed",  
  "data": {  
    "object": {  
      "object": "callSummary",  
      "callId": "AC641569d25e0a4489ad807409d9bee3fd",  
      "status": "completed",  
      "summary": [  
        "this is your call summary."  
      ],  
      "nextSteps": [  
        "these are your next steps."  
      ]  
    }  
  }  
}
call.transcript.completed payload:
{  
  "id": "EV67e3564088bf4badb6593cd7db8f2832",  
  "object": "event",  
  "apiVersion": "v3",  
  "createdAt": "2024-09-05T15:30:18.629Z",  
  "type": "call.transcript.completed",  
  "data": {  
    "object": {  
      "object": "callTranscript",  
      "callId": "AC741569d25e0a4489ad804709d9bee3fc",  
      "createdAt": "2024-09-05T15:30:18.198Z",  
      "dialogue": [  
        {  
          "end": 73.57498,  
          "start": 0.03998,  
          "userId": "UStpTEr9a6",  
          "content": "hello world",  
          "identifier": "+12345678901"  
        },  
        {  
          "end": 1.52,  
          "start": 1.12,  
          "userId": "USiAYGldYE",  
          "content": "hi there",  
          "identifier": "+19876543210"  
        }  
      ],  
      "duration": 87.614685,  
      "status": "completed"  
    }  
  }  
}

Additional resources

Need help? Submit a support request at support.openphone.com for technical assistance with webhook implementation.