You can synchronize your support cases in real time between Google Cloud and your Customer Relationship Management (CRM) system (such as Jira Service Desk, Zendesk, or ServiceNow) by building an event-driven connector using Pub/Sub and Cloud Customer Care's event subscription service.
Advantages of an event-driven connector
Compared to a polling-based connector, an event-driven connector offers several significant benefits:
- Real-time synchronization: Your CRM is updated immediately when a case, comment, or attachment is created or modified in Customer Care, rather than waiting for the next polling interval.
- Reduced API quota consumption: You don't need to continually query the Cloud Support API (CSAPI) to check for changes, which preserves your API quota for actual read and write operations.
- Simpler scheduling: You don't need to manage periodic background jobs or cron schedules to continuously poll Google Cloud for updates.
High-level architecture
The event-driven connector relies on Google Cloud's Event Subscription service and Pub/Sub to deliver real-time notifications to your application.
When an action occurs on a support case in Google Cloud, the Event Subscription service publishes a notification message to a customer-owned Pub/Sub topic. Pub/Sub then delivers this message to your connector application—typically through a push subscription to a webhook endpoint hosted on Cloud Run, App Engine, or your internal infrastructure.
The following sequence describes the event-driven synchronization flow from Google Cloud to your CRM:
- A user or system performs an action on a case in Customer Care (for example, creating a case, adding a comment, or uploading an attachment).
- The Cloud Support API publishes an event notification to your configured Pub/Sub topic.
- Pub/Sub pushes the notification to your connector's webhook endpoint.
- The connector receives the notification, extracts the resource name from the message attributes, and calls the Cloud Support API to retrieve the full, latest details of the modified resource.
- The connector updates your CRM system with the retrieved data.
When changes occur in your CRM, your connector should continue to push those updates to Customer Care by directly calling the Cloud Support API (for example, calling cases.patch or cases.comments.create), just as in the polling-based architecture.
Prerequisites and setup
To set up an event-driven connector, you must configure the necessary Google Cloud resources and Identity and Access Management (IAM) permissions.
1. Enable required APIs
Ensure that both the Cloud Support API and the Pub/Sub API are enabled in the Google Cloud project where you are deploying your connector and Pub/Sub topic:
cloudsupport.googleapis.compubsub.googleapis.com
2. Create a Pub/Sub topic
Create a Pub/Sub topic in your Google Cloud project to receive support event notifications:
gcloud pubsub topics create support-events \
--project=[PROJECT_ID]
Replace [PROJECT_ID] with your Google Cloud project ID.
3. Grant publisher permissions to Customer Care
To allow Customer Care to publish event notifications to your Pub/Sub topic, you must grant the Pub/Sub Publisher role (roles/pubsub.publisher) on your topic to the Google Support event publishing service account:
cloud-support-apievents@system.gserviceaccount.com
Run the following Google Cloud CLI command to grant this permission:
gcloud pubsub topics add-iam-policy-binding projects/[PROJECT_ID]/topics/[TOPIC_NAME] \
--member="serviceAccount:cloud-support-apievents@system.gserviceaccount.com" \
--role="roles/pubsub.publisher"
Replace [TOPIC_NAME] with the name of your Pub/Sub topic (for example, support-events).
4. Create a support event subscription
Once your topic is created and permissions are granted, create a SupportEventSubscription resource using the Cloud Support API to begin receiving event notifications for your organization.
You can create the subscription using the REST API:
curl -X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
"https://cloudsupport.googleapis.com/v2/organizations/[ORGANIZATION_ID]/supportEventSubscriptions" \
-d '{
"pubSubTopic": "projects/[PROJECT_ID]/topics/[TOPIC_NAME]"
}'
Replace [ORGANIZATION_ID] with your Google Cloud organization ID.
If successful, the API returns the created SupportEventSubscription resource. Ensure that the state field in the response is set to WORKING:
{
"name": "organizations/123456789/supportEventSubscriptions/987654321",
"pubSubTopic": "projects/my-project/topics/support-events",
"state": "WORKING",
"createTime": "2026-07-08T12:00:00Z",
"updateTime": "2026-07-08T12:00:00Z"
}
Pub/Sub message format
When an event occurs, Customer Care publishes a standard Pub/Sub message to your topic. To minimize payload size and avoid transmitting sensitive data directly through Pub/Sub, the message data field is empty. Instead, all event metadata is provided in the message attributes.
The attributes map contains the following key-value pairs:
| Attribute | Description | Example |
|---|---|---|
resource_name |
The full relative resource name of the Cloud Support API resource that was created or modified. | organizations/123/cases/456organizations/123/cases/456/comments/789organizations/123/cases/456/attachments/012 |
notification_type |
The type of event that triggered the notification. | CASE_CREATEDCASE_UPDATEDCOMMENT_ADDEDATTACHMENT_ADDEDATTACHMENT_DELETED |
Notification types
The notification_type attribute indicates what action occurred:
CASE_CREATED: A new support case was created.CASE_UPDATED: An existing case was updated (for example, priority, status, or title changed).COMMENT_ADDED: A new comment was added to a case.ATTACHMENT_ADDED: A new attachment was uploaded to a case.ATTACHMENT_DELETED: An attachment was deleted from a case.
Connector implementation example
The following example demonstrates how to implement a webhook handler in Python using the Flask framework to receive Pub/Sub push notifications and synchronize changes to your CRM.
Webhook handler
When configuring your Pub/Sub push subscription, point the endpoint URL to your Flask application's /webhook route:
import base64
import json
from flask import Flask, jsonify, request
import googleapiclient.discovery
app = Flask(__name__)
# Initialize the Cloud Support API client
api_version = "v2"
support_api = googleapiclient.discovery.build(
serviceName="cloudsupport",
version=api_version,
discoveryServiceUrl=f"https://cloudsupport.googleapis.com/$discovery/rest?version={api_version}",
)
@app.route("/webhook", methods=["POST"])
def pubsub_push_handler():
"""Handles incoming Pub/Sub push messages."""
envelope = request.get_json()
if not envelope:
return "Bad Request: no envelope received", 400
pubsub_message = envelope.get("message")
if not pubsub_message:
return "Bad Request: no message received", 400
attributes = pubsub_message.get("attributes", {})
resource_name = attributes.get("resource_name")
notification_type = attributes.get("notification_type")
if not resource_name or not notification_type:
# Acknowledge message to prevent Pub/Sub from retrying malformed messages
return "Missing required attributes", 200
try:
# Process the event based on resource type and notification type
process_support_event(resource_name, notification_type)
except Exception as e:
# Log error and return 500 so Pub/Sub will retry delivery
print(f"Error processing event for {resource_name}: {e}")
return "Internal Server Error", 500
# Acknowledge successful processing to Pub/Sub
return "", 204
def process_support_event(resource_name, notification_type):
"""Fetches resource details from CSAPI and synchronizes with CRM."""
if "/comments/" in resource_name:
sync_comment(resource_name, notification_type)
elif "/attachments/" in resource_name:
sync_attachment(resource_name, notification_type)
elif "/cases/" in resource_name:
sync_case(resource_name, notification_type)
else:
print(f"Unrecognized resource type in name: {resource_name}")
def sync_case(case_name, notification_type):
"""Retrieves case details and updates CRM."""
case = support_api.cases().get(name=case_name).execute()
# Implement CRM-specific logic to create or update the case
# For example:
# if notification_type == "CASE_CREATED":
# crm_client.create_ticket(summary=case.get("displayName"), ...)
# elif notification_type == "CASE_UPDATED":
# crm_client.update_ticket(status=case.get("state"), ...)
print(f"Synced case {case_name} ({notification_type})")
def sync_comment(comment_name, notification_type):
"""Retrieves comment details and adds it to CRM."""
if notification_type == "COMMENT_ADDED":
comment = support_api.cases().comments().get(name=comment_name).execute()
# Extract parent case name (e.g., organizations/123/cases/456)
case_name = comment_name.split("/comments/")[0]
# Implement CRM-specific logic to append the comment to the corresponding ticket
# crm_client.add_comment(ticket_id=lookup_crm_id(case_name), body=comment.get("body"))
print(f"Synced comment {comment_name} for case {case_name}")
def sync_attachment(attachment_name, notification_type):
"""Handles attachment additions or deletions in CRM."""
if notification_type == "ATTACHMENT_ADDED":
attachment = support_api.cases().attachments().get(name=attachment_name).execute()
case_name = attachment_name.split("/attachments/")[0]
# Implement CRM-specific logic to download media from CSAPI and attach to CRM ticket
print(f"Synced attachment {attachment_name} for case {case_name}")
elif notification_type == "ATTACHMENT_DELETED":
# Handle attachment removal if supported by your CRM
print(f"Processed attachment deletion: {attachment_name}")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
Handling outages and circuit breakers
Customer Care includes built-in circuit breaker mechanisms to protect both your infrastructure and Google's systems during outages or misconfigurations.
Subscription failure states
If Customer Care is repeatedly unable to publish messages to your Pub/Sub topic (for example, if the topic is deleted or the IAM publishing permission is revoked), the state of your SupportEventSubscription transitions from WORKING to FAILING. When in the FAILING state, Customer Care temporarily suspends publishing event notifications for that subscription.
You can inspect the failure_reason field of the subscription to determine why publishing is failing:
PERMISSION_DENIED: The service account (cloud-support-apievents@system.gserviceaccount.com) lacks thepubsub.publisherpermission on your topic.TOPIC_NOT_FOUND: The configured Pub/Sub topic does not exist.OTHER: Publishing failed due to another transient or system-side error.
Monitoring and recovery
To ensure reliable synchronization, we recommend setting up monitoring and alerting:
- Monitor event processing and subscription health: Monitor event processing throughput, latency, and error rates on your connector service to alert your team if processing drops below expected baselines. You can also periodically query your
SupportEventSubscriptionresource using the Cloud Support API to verify if any subscription enters theFAILINGstate. - Resolve underlying issues: Restore the missing IAM permissions or recreate the missing Pub/Sub topic.
- Resume synchronization: Once the underlying issue is resolved, issue an update call to
UpdateSupportEventSubscriptionproviding yourpub_sub_topic(a no-op update with your existing topic name is sufficient). When called, Customer Care sends a test notification to verify topic access, automatically resetting the subscriptionstateback toWORKINGand clearingfailure_reasononce verified.
If an outage occurs and your webhook is temporarily unreachable, Pub/Sub automatically retries message delivery according to your topic and subscription's retry policies. If an outage exceeds your Pub/Sub message retention window, your connector should perform a fallback polling query (using cases.search with a time filter) to catch up on any missed updates during the downtime.
What's next
- Learn how to Synchronize cases using polling.
- Review the Cloud Support API user guide.
- Learn more about Access control with IAM.