{"id":97,"date":"2026-08-03T07:38:00","date_gmt":"2026-08-03T04:38:00","guid":{"rendered":"https:\/\/john-nessime.com\/blog\/?p=97"},"modified":"2026-08-02T11:47:34","modified_gmt":"2026-08-02T08:47:34","slug":"monitor-salesforce-integrations","status":"publish","type":"post","link":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/","title":{"rendered":"Zero Errors, Zero Records: Monitoring Salesforce Integrations with CloudWatch and Grafana"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Sales ops asks why an account they created three weeks ago still isn&#8217;t in the warehouse. You open the dashboard for the sync. Error count: zero. Every day, flat, zero. The alarm is not firing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then you check the invocation count and it is also zero, and has been since the day somebody disabled an EventBridge rule while cleaning up a different stack. The job has not run in three weeks. It never errored because it never started.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is the shape of almost every integration monitoring failure: <strong>a healthy graph and a stopped job produce identical output.<\/strong> Zero errors is what success looks like and it is also what absence looks like, and if the only thing you measure is failure, the two are indistinguishable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is about how to <strong>monitor Salesforce integrations<\/strong> so that stopping is as loud as breaking. Four signals worth emitting, how to get them into CloudWatch cheaply, the alarm configuration that actually fires, and what belongs on a Grafana dashboard once you have them.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Four signals, not one<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most integration monitoring stops at errors and duration, because those come free from Lambda or your container platform. Both are worth having and neither answers the question anybody actually asks, which is &#8220;is the data right&#8221;.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The four that do:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Liveness.<\/strong> Did it run at all?<\/li>\n<li><strong>Volume.<\/strong> Did it move a plausible amount of data?<\/li>\n<li><strong>Freshness.<\/strong> How old is the newest record on the destination side?<\/li>\n<li><strong>Budget.<\/strong> How much of Salesforce&#8217;s daily API allowance have you spent?<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Errors are a fifth, and the least interesting, because errors are the failure mode that already announces itself.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Liveness: the alarm that has to fire on silence<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Emit a metric on every successful completion. A single count, value 1. Then alarm when it stops arriving.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The trap is in the CloudWatch defaults. <code>TreatMissingData<\/code> has four settings, and the default is <code>missing<\/code>, which sends the alarm to <code>INSUFFICIENT_DATA<\/code> when nothing arrives. That state is not <code>ALARM<\/code>. Nothing pages. Your dashboard shows a grey alarm that most people read as &#8220;fine&#8221;.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So heartbeat alarms need <code>breaching<\/code>. That much is standard advice. Here is the part that is not: <strong>even with <code>breaching<\/code> set, a heartbeat alarm can still fail to fire.<\/strong> CloudWatch evaluates over a range wider than your evaluation periods, and if it finds any real data point in that wider range, those override the missing ones. On a job that runs hourly, a successful run from earlier can keep the alarm quiet through several missed runs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The robust version uses metric math to turn absence into a real zero, so there is no missing data to interpret:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>aws cloudwatch put-metric-alarm \n  --alarm-name \"opportunity-sync-not-running\" \n  --alarm-description \"No completed run in the last 90 minutes\" \n  --comparison-operator LessThanThreshold \n  --threshold 1 \n  --evaluation-periods 1 \n  --treat-missing-data breaching \n  --alarm-actions \"$SNS_TOPIC_ARN\" \n  --metrics '[\n    {\n      \"Id\": \"runs\",\n      \"MetricStat\": {\n        \"Metric\": {\n          \"Namespace\": \"SalesforceSync\",\n          \"MetricName\": \"RunCompleted\",\n          \"Dimensions\": [{\"Name\": \"Integration\", \"Value\": \"opportunity-sync\"}]\n        },\n        \"Period\": 5400,\n        \"Stat\": \"Sum\"\n      },\n      \"ReturnData\": false\n    },\n    {\n      \"Id\": \"filled\",\n      \"Expression\": \"FILL(runs, 0)\",\n      \"ReturnData\": true\n    }\n  ]'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>FILL(runs, 0)<\/code> substitutes a zero wherever the metric has no data point, so the alarm always has something real to compare against the threshold. The window is deliberately longer than the schedule: an hourly job gets ninety minutes, so one late run does not wake anybody.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then do the thing everyone skips: disable the schedule in a test account and confirm the alarm actually goes red. An untested alarm is a belief, not a control.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Volume and freshness: is the data actually moving<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A job can complete successfully and process nothing. A credential with the wrong field-level permissions, a filter that silently matches nothing, a watermark that got written before the data landed: all of these produce a clean run and an empty result.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Volume<\/strong> is the count of records read and written per run. Alarming on it is harder than liveness because the right number varies: a quiet Sunday legitimately looks like a broken Tuesday. Two approaches that work. Use CloudWatch anomaly detection, which learns the daily and weekly shape and alarms on departures from it. Or set a crude floor that only catches the catastrophic case, which is usually zero, and accept that you will not catch a fifty percent drop.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I would start with the crude floor. It is five minutes of work and catches the failure that actually happens.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Freshness<\/strong> is the better metric and almost nobody emits it. At the end of each run, query the destination for the newest record&#8217;s modified timestamp, subtract it from now, and publish the difference in seconds. That single number answers the business question directly: how far behind Salesforce are we right now?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It also collapses several failure modes into one signal. A stopped job, a job that runs but writes nothing, a job stuck retrying, a job silently filtered down to zero rows: all of them show up as lag climbing. If you only add one metric from this post, add this one.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Budget: watch the Salesforce allowance<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Your org has a daily API allowance shared across every integration touching it. Exceeding it does not just break your sync; it breaks marketing automation, support tooling, and whatever else somebody connected two years ago.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enforcement is initially soft, and then it is not: past a protection threshold, calls come back as 403 with <code>REQUEST_LIMIT_EXCEEDED<\/code> until the rolling window drains. By that point you are in an incident that spans several teams.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The good news is that this costs nothing to observe. Salesforce returns your current consumption on ordinary REST responses in a header, so you get it on calls you were making anyway:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Sforce-Limit-Info: api-usage=1212\/15000\n#\n# Free: no extra API call, which matters when the thing you are\n# measuring is an API budget. Add real error handling before\n# shipping this; the header is not guaranteed on every response.\nraw = response.headers.get(\"Sforce-Limit-Info\", \"\")\nused, allowed = (int(v) for v in raw.split(\"api-usage=\")[1].split(\"\/\"))\nemit(\"ApiUsagePercent\", used \/ allowed * 100)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For a fuller picture, the <code>\/services\/data\/vXX.X\/limits<\/code> endpoint returns every allocation in the org, including <code>DailyApiRequests<\/code> with its max and remaining values. It needs the View Setup and Configuration permission and the numbers lag by a few minutes. Poll it on a schedule rather than per request, and alarm on percentage consumed rather than absolute calls, so the alarm survives a licence change.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Graph consumption by integration if you can attribute it. The conversation about which team is burning the allowance goes very differently when there is a chart.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Getting the metrics in without a bill shock<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You can call <code>PutMetricData<\/code> directly, and it works, and it is a synchronous API call in the hot path of your job that can fail or add latency. Custom metrics are also charged per metric per month, and a metric is every unique combination of name and dimensions, so a dimension with high cardinality gets expensive quietly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The better default is Embedded Metric Format: write structured JSON to stdout and CloudWatch extracts the metrics from your logs. No API call, no added latency, and the log line stays queryable in Logs Insights alongside the metric.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"_aws\": {\n    \"Timestamp\": 1700000000000,\n    \"CloudWatchMetrics\": [{\n      \"Namespace\": \"SalesforceSync\",\n      \"Dimensions\": [[\"Integration\"]],\n      \"Metrics\": [\n        { \"Name\": \"RunCompleted\",     \"Unit\": \"Count\"   },\n        { \"Name\": \"RecordsWritten\",   \"Unit\": \"Count\"   },\n        { \"Name\": \"SourceLagSeconds\", \"Unit\": \"Seconds\" },\n        { \"Name\": \"ApiUsagePercent\",  \"Unit\": \"Percent\" }\n      ]\n    }]\n  },\n  \"Integration\": \"opportunity-sync\",\n  \"RunId\": \"a41c9f\",\n  \"RunCompleted\": 1,\n  \"RecordsWritten\": 4127,\n  \"SourceLagSeconds\": 312,\n  \"ApiUsagePercent\": 8.1\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note what is a dimension and what is not. <code>Integration<\/code> is a dimension because it has a handful of values and you want to alarm per integration. <code>RunId<\/code> is a plain field: searchable in the logs, and not a dimension, because making it one would create a new metric on every run. That distinction is the whole cost story.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Grafana on top<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">CloudWatch dashboards are fine and Grafana is better for this, for three reasons: you can put Salesforce metrics next to your warehouse and application metrics on one screen, the alerting is more expressive, and non-engineers will actually open it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Add CloudWatch as a data source using an IAM role rather than access keys, scoped to <code>cloudwatch:GetMetricData<\/code>, <code>cloudwatch:ListMetrics<\/code> and the Logs Insights permissions if you want log panels. One honest cost note: Grafana queries CloudWatch through the metric data API, which is billed per metric requested, so a busy dashboard on a short refresh interval is a real line item. Set a sane refresh, avoid auto-refresh on wall displays, and use the caching in Grafana&#8217;s CloudWatch data source.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">What goes on the dashboard, in order down the page:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Freshness per integration<\/strong>, as a stat panel with thresholds. This is the panel people look at.<\/li>\n<li><strong>Time since last successful run<\/strong>, per integration.<\/li>\n<li><strong>Records processed<\/strong>, over a window long enough to show the weekly shape.<\/li>\n<li><strong>API allowance consumed<\/strong>, as a percentage with a threshold line.<\/li>\n<li><strong>Errors and duration<\/strong>, at the bottom, where they belong.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">One dashboard, one screen, no scrolling. A dashboard nobody can read at a glance during an incident is decoration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Alerts people don&#8217;t ignore<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Decide deliberately where alerting lives. CloudWatch alarms are more reliable, because they keep working when Grafana is down, and Grafana alerts are more flexible and can span data sources. My default is CloudWatch for the small number of alerts that page someone, and Grafana for everything informational.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Three things that separate a useful alert from noise. Alarm on the symptom, not the cause: &#8220;Opportunity data is more than two hours stale&#8221; is actionable in a way &#8220;Lambda errors greater than zero&#8221; is not. Put the runbook link in the alarm description, since that field ends up in the notification and is the only documentation anybody reads at midnight. And use composite alarms to suppress the cascade, so a Salesforce outage produces one page rather than nine.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common mistakes<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Monitoring only errors, so a stopped job looks identical to a healthy one.<\/li>\n<li>Leaving <code>TreatMissingData<\/code> at its default on a heartbeat alarm.<\/li>\n<li>Setting it to <code>breaching<\/code> and assuming that is sufficient, without handling the evaluation range.<\/li>\n<li>Never testing that an alarm fires by actually breaking something.<\/li>\n<li>No freshness metric, so nobody can answer how far behind the data is.<\/li>\n<li>Ignoring API allowance until an integration you do not own breaks.<\/li>\n<li>High-cardinality dimensions such as record ID or run ID, and the bill that follows.<\/li>\n<li>Calling <code>PutMetricData<\/code> synchronously in the job&#8217;s critical path.<\/li>\n<li>Alerting on causes rather than on user-visible symptoms.<\/li>\n<li>A dashboard that requires scrolling and interpretation during an incident.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Best practices<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Emit liveness, volume, freshness and API budget from every integration, as a standard.<\/li>\n<li>Heartbeat alarms with <code>breaching<\/code> plus <code>FILL()<\/code>, and a window longer than the schedule.<\/li>\n<li>Freshness as the headline metric, because it maps to a question the business asks.<\/li>\n<li>Embedded Metric Format rather than direct API calls.<\/li>\n<li>Low-cardinality dimensions; everything else stays a log field.<\/li>\n<li>Alarm on percentage of the API allowance, not absolute calls.<\/li>\n<li>IAM roles for the Grafana data source, and a refresh interval you have costed.<\/li>\n<li>Runbook links in alarm descriptions.<\/li>\n<li>Composite alarms to collapse cascades into one page.<\/li>\n<li>A quarterly test that breaks each integration on purpose and confirms someone gets told.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">FAQ<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Why didn&#8217;t my CloudWatch alarm fire when the job stopped?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Almost certainly <code>TreatMissingData<\/code>. The default sends the alarm to <code>INSUFFICIENT_DATA<\/code>, which is not <code>ALARM<\/code> and pages nobody. Set it to <code>breaching<\/code>, and wrap the metric in <code>FILL()<\/code> so there is no missing data for CloudWatch to reinterpret.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What&#8217;s the single most useful metric to add?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Freshness: how old the newest record on the destination side is. It catches stopped jobs, empty runs, stuck retries and silent filtering with one number, and it is the only one of these metrics a non-engineer can interpret.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">CloudWatch dashboards or Grafana?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Grafana if you already run it, because you can put Salesforce, warehouse and application metrics on one screen. CloudWatch if you do not, because a second system to operate is not free. Either way keep the paging alarms in CloudWatch so they survive Grafana being down.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Will custom metrics be expensive?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Only if you make them so. Cost scales with unique name-and-dimension combinations, so a handful of metrics dimensioned by integration name is negligible. Adding a run ID or record ID as a dimension is how the bill grows without anyone noticing.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How do I monitor a third-party connector I can&#8217;t add code to?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Monitor the destination instead. A scheduled job that queries the target for the newest record&#8217;s timestamp and emits it as a freshness metric works regardless of what wrote the data, and it is arguably a better test because it measures the outcome rather than the process.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">The one thing to remember<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Absence of failure is not evidence of success. An integration that stopped produces exactly the same error graph as one working perfectly, and every default in your monitoring stack is tuned to stay quiet when data stops arriving rather than to shout about it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So measure the thing you actually care about. Not &#8220;did it error&#8221; but &#8220;how stale is the data right now&#8221;, alarmed in a way that fires on silence, and tested by deliberately breaking it. Everything else on the dashboard is supporting evidence.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Want this built properly?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Integration monitoring tends to get added after the first silent failure, which is one failure too late. Work I take on:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Instrumenting Salesforce integrations with liveness, volume, freshness and API budget metrics via CloudWatch.<\/li>\n<li>Auditing existing alarms for the ones that cannot fire, and fixing the missing-data handling.<\/li>\n<li>Building the Grafana dashboard and data source, including cost-aware query and refresh configuration.<\/li>\n<li>Alert design: symptom-based alarms, composite alarms to suppress cascades, runbooks attached where people will read them.<\/li>\n<li>API allowance monitoring and attribution across multiple integrations sharing one org.<\/li>\n<li>Running a failure drill so you know the alerting works before you need it.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Tell me how you would currently find out that a sync stopped, and I will tell you how long it would take.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.upwork.com\/freelancers\/~01f15a912ad84a6620\" target=\"_blank\" rel=\"noreferrer noopener\">Work with me on Upwork<\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>The error count was zero every day for three weeks. So was the invocation count. A stopped integration and a healthy one produce identical graphs, and every CloudWatch default is tuned to stay quiet when data stops arriving. Four signals worth emitting, and the alarm config that actually fires.<\/p>\n","protected":false},"author":1,"featured_media":98,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[25,24,52],"tags":[98,93,186,19,185,155,3,157,11,21,116,13,96,159,118,4],"class_list":["post-97","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cloud-computing","category-devops","category-technical-guides","tag-alerting","tag-aws","tag-aws-lambda","tag-cloud","tag-cloudwatch","tag-data-integration","tag-devops","tag-etl","tag-grafana","tag-infrastructure","tag-logging","tag-monitoring","tag-observability","tag-salesforce","tag-sre","tag-troubleshooting","entry","has-media"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Monitor Salesforce Integrations: CloudWatch + Grafana<\/title>\n<meta name=\"description\" content=\"Monitor Salesforce integrations properly: heartbeat metrics that catch a sync that stopped, freshness, API limits, CloudWatch alarms and Grafana dashboards.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Monitor Salesforce Integrations: CloudWatch + Grafana\" \/>\n<meta property=\"og:description\" content=\"Monitor Salesforce integrations properly: heartbeat metrics that catch a sync that stopped, freshness, API limits, CloudWatch alarms and Grafana dashboards.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/\" \/>\n<meta property=\"og:site_name\" content=\"John Nessime\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-03T04:38:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/monitor-salesforce-integrations.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"627\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"John Nessime\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"John Nessime\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/monitor-salesforce-integrations\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/monitor-salesforce-integrations\\\/\"},\"author\":{\"name\":\"John Nessime\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\"},\"headline\":\"Zero Errors, Zero Records: Monitoring Salesforce Integrations with CloudWatch and Grafana\",\"datePublished\":\"2026-08-03T04:38:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/monitor-salesforce-integrations\\\/\"},\"wordCount\":2140,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\"},\"image\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/monitor-salesforce-integrations\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/monitor-salesforce-integrations.png\",\"keywords\":[\"Alerting\",\"AWS\",\"AWS Lambda\",\"Cloud\",\"CloudWatch\",\"Data Integration\",\"DevOps\",\"ETL\",\"Grafana\",\"Infrastructure\",\"Logging\",\"Monitoring\",\"Observability\",\"Salesforce\",\"SRE\",\"Troubleshooting\"],\"articleSection\":[\"Cloud Computing\",\"DevOps\",\"Technical Guides\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/monitor-salesforce-integrations\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/monitor-salesforce-integrations\\\/\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/monitor-salesforce-integrations\\\/\",\"name\":\"Monitor Salesforce Integrations: CloudWatch + Grafana\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/monitor-salesforce-integrations\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/monitor-salesforce-integrations\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/monitor-salesforce-integrations.png\",\"datePublished\":\"2026-08-03T04:38:00+00:00\",\"description\":\"Monitor Salesforce integrations properly: heartbeat metrics that catch a sync that stopped, freshness, API limits, CloudWatch alarms and Grafana dashboards.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/monitor-salesforce-integrations\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/monitor-salesforce-integrations\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/monitor-salesforce-integrations\\\/#primaryimage\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/monitor-salesforce-integrations.png\",\"contentUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/monitor-salesforce-integrations.png\",\"width\":1200,\"height\":627,\"caption\":\"Chart showing records synced dropping to zero while the error count stays flat at zero, illustrating that a stopped integration and a healthy one produce identical error graphs.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/monitor-salesforce-integrations\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Zero Errors, Zero Records: Monitoring Salesforce Integrations with CloudWatch and Grafana\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/\",\"name\":\"John Nessime\",\"description\":\"Cloud, DevOps, Data &amp; AI \u2014 Built, Tested, Explained\",\"publisher\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\",\"name\":\"John Nessime\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\",\"contentUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\",\"width\":512,\"height\":512,\"caption\":\"John Nessime\"},\"logo\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\"},\"description\":\"AWS Certified Solutions Architect helping businesses build reliable cloud, data, reporting, and automation solutions. I help startups, agencies, and growing businesses replace manual processes and disconnected data with practical AWS architectures, clean data pipelines, useful dashboards, and maintainable automation.\",\"sameAs\":[\"https:\\\/\\\/john-nessime.com\\\/blog\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/john-m-nessime\"],\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/author\\\/johnnessime\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Monitor Salesforce Integrations: CloudWatch + Grafana","description":"Monitor Salesforce integrations properly: heartbeat metrics that catch a sync that stopped, freshness, API limits, CloudWatch alarms and Grafana dashboards.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/","og_locale":"en_US","og_type":"article","og_title":"Monitor Salesforce Integrations: CloudWatch + Grafana","og_description":"Monitor Salesforce integrations properly: heartbeat metrics that catch a sync that stopped, freshness, API limits, CloudWatch alarms and Grafana dashboards.","og_url":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/","og_site_name":"John Nessime","article_published_time":"2026-08-03T04:38:00+00:00","og_image":[{"width":1200,"height":627,"url":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/monitor-salesforce-integrations.png","type":"image\/png"}],"author":"John Nessime","twitter_card":"summary_large_image","twitter_misc":{"Written by":"John Nessime","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/#article","isPartOf":{"@id":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/"},"author":{"name":"John Nessime","@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105"},"headline":"Zero Errors, Zero Records: Monitoring Salesforce Integrations with CloudWatch and Grafana","datePublished":"2026-08-03T04:38:00+00:00","mainEntityOfPage":{"@id":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/"},"wordCount":2140,"commentCount":0,"publisher":{"@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105"},"image":{"@id":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/#primaryimage"},"thumbnailUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/monitor-salesforce-integrations.png","keywords":["Alerting","AWS","AWS Lambda","Cloud","CloudWatch","Data Integration","DevOps","ETL","Grafana","Infrastructure","Logging","Monitoring","Observability","Salesforce","SRE","Troubleshooting"],"articleSection":["Cloud Computing","DevOps","Technical Guides"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/","url":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/","name":"Monitor Salesforce Integrations: CloudWatch + Grafana","isPartOf":{"@id":"https:\/\/john-nessime.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/#primaryimage"},"image":{"@id":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/#primaryimage"},"thumbnailUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/monitor-salesforce-integrations.png","datePublished":"2026-08-03T04:38:00+00:00","description":"Monitor Salesforce integrations properly: heartbeat metrics that catch a sync that stopped, freshness, API limits, CloudWatch alarms and Grafana dashboards.","breadcrumb":{"@id":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/#primaryimage","url":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/monitor-salesforce-integrations.png","contentUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/monitor-salesforce-integrations.png","width":1200,"height":627,"caption":"Chart showing records synced dropping to zero while the error count stays flat at zero, illustrating that a stopped integration and a healthy one produce identical error graphs."},{"@type":"BreadcrumbList","@id":"https:\/\/john-nessime.com\/blog\/devops\/monitor-salesforce-integrations\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/john-nessime.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Zero Errors, Zero Records: Monitoring Salesforce Integrations with CloudWatch and Grafana"}]},{"@type":"WebSite","@id":"https:\/\/john-nessime.com\/blog\/#website","url":"https:\/\/john-nessime.com\/blog\/","name":"John Nessime","description":"Cloud, DevOps, Data &amp; AI \u2014 Built, Tested, Explained","publisher":{"@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/john-nessime.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105","name":"John Nessime","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png","url":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png","contentUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png","width":512,"height":512,"caption":"John Nessime"},"logo":{"@id":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png"},"description":"AWS Certified Solutions Architect helping businesses build reliable cloud, data, reporting, and automation solutions. I help startups, agencies, and growing businesses replace manual processes and disconnected data with practical AWS architectures, clean data pipelines, useful dashboards, and maintainable automation.","sameAs":["https:\/\/john-nessime.com\/blog","https:\/\/www.linkedin.com\/in\/john-m-nessime"],"url":"https:\/\/john-nessime.com\/blog\/author\/johnnessime\/"}]}},"_links":{"self":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts\/97","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/comments?post=97"}],"version-history":[{"count":1,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts\/97\/revisions"}],"predecessor-version":[{"id":102,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts\/97\/revisions\/102"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/media\/98"}],"wp:attachment":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/media?parent=97"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/categories?post=97"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/tags?post=97"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}