{"id":19474,"date":"2019-02-15T13:17:59","date_gmt":"2019-02-15T13:17:59","guid":{"rendered":"https:\/\/www.intercom.com\/blog\/?p=19474"},"modified":"2020-07-30T12:54:44","modified_gmt":"2020-07-30T11:54:44","slug":"how-we-used-dynamodb-streams","status":"publish","type":"post","link":"https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/","title":{"rendered":"How we used DynamoDB Streams to visualize changes in frequently updated objects"},"content":{"rendered":"<p>One of the most fundamental elements of <a href=\"https:\/\/www.intercom.com\/blog\/customer-intelligence\" target=\"_blank\" rel=\"noopener noreferrer\">the Intercom platform<\/a> is how it handles <a href=\"https:\/\/www.intercom.com\/blog\/help\/intercom-s-key-features-explained\/tracking-user-data\/tracking-user-data-in-intercom\" target=\"_blank\" rel=\"noopener noreferrer\">user data<\/a> \u2013 for us and our customers, the ability to access, track and filter data about users is what makes Intercom such a powerful solution.<\/p>\n<p>Some important functionality hinges on matching user data against certain criteria \u2013 it\u2019s what allows us <a href=\"https:\/\/www.intercom.com\/blog\/help\/intercom-s-key-features-explained\/sending-messages\/how-user-auto-messages-work\" target=\"_blank\" rel=\"noopener noreferrer\">to send auto-messages to specific users<\/a>, for example. That means the process of updating the user state is extremely important, and a single user can be updated thousands of times per day. We primarily use <a href=\"https:\/\/aws.amazon.com\/dynamodb\/\" target=\"_blank\" rel=\"noopener noreferrer\">DynamoDB<\/a> to store the latest user state.<\/p>\n<p>However, we realized that our Customer Support team needed more insight into the historical state of a user at a specific point in time, particularly when they needed to troubleshoot problems or verify why a specific user <a href=\"https:\/\/www.intercom.com\/blog\/help\/intercom-s-key-features-explained\/grouping-users\/how-to-filter-and-segment-your-users\" target=\"_blank\" rel=\"noopener noreferrer\">matched certain message criteria<\/a> at a previous time.<\/p>\n<blockquote class=\"pullquote-style-one\"><p>&#8220;We wanted to allow our customer support team to self serve when digging into the historical user states&#8221;<\/p><\/blockquote>\n<p>This was not easy to do given the volume of user data. It required our engineers to dig into logs and combine all the relevant information of what happened to a user object at any given moment in time. Grepping logs is not scalable and involves a lot of manual work, which is prone to mistakes.<\/p>\n<p>We thought there was probably a better way to automate a lot of the investigative work that was involved here by more efficiently surfacing historical changes in user states. We wanted to allow our customer support team to self serve when digging into the historical user states.<\/p>\n<h2 id=\"leveraging-existing-technologies\">Leveraging existing technologies<\/h2>\n<p>In exploring ways to better help our customer support team, we had two main requirements:<\/p>\n<ol>\n<li>Allow access to historical user state changes, reliably and quickly<\/li>\n<li>Display this information in an easy-to-comprehend way<\/li>\n<\/ol>\n<p>The self service tool needed to be optimized for writes. Users are updated frequently, but the need to troubleshoot them is infrequent. Historical user states would need to be retrieved a maximum of a few times per day, but often with days of no activity at all.<\/p>\n<p>To support that, we wanted to be able to get the real-time updates of a user.\u00a0<a href=\"https:\/\/aws.amazon.com\/blogs\/database\/dynamodb-streams-use-cases-and-design-patterns\/\" target=\"_blank\" rel=\"noopener noreferrer\">DynamoDB Streams<\/a> is a service that allows you to capture this table activity. Each update for a user is captured in a DynamoDB Stream event. It was a natural solution that\u00a0we could leverage to develop our internal tool, called the user history tool, or UHT for short.<\/p>\n<h2 id=\"building-our-user-history-tool\">Building our user history tool<\/h2>\n<p>When we receive a DynamoDB Stream event for a table activity, we store the update that happened to the user.\u00a0We needed to persist these real-time updates <em>without<\/em> having to store all the intermediate states of the user objects because with so many updates to user states, a database table would grow too fast. In response to each event, a lambda function is triggered, where we do the processing.\u00a0This operation has two main responsibilities:<\/p>\n<ol>\n<li>Calculate the diff between the old user state and new user state in the Stream<\/li>\n<li>Store the calculated diff in a dedicated user history DynamoDB table<\/li>\n<\/ol>\n<p>Since the records in a DynamoDB stream are in a JSON format, we obtain the diff of two JSON objects. The diff produced is in a <a href=\"http:\/\/jsonpatch.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">JSON Patch<\/a> format. The patch looks like:<\/p>\n<p><code>[<br \/>\n{ \"op\": \"replace\", \"path\": \"\/baz\", \"value\": \"boo\" },<br \/>\n{ \"op\": \"add\", \"path\": \"\/hello\", \"value\": [\"world\"] }<br \/>\n{ \"op\": \"delete\", \"path\": \"\/foo\"}<br \/>\n]<br \/>\n<\/code><\/p>\n<p>Together with the calculated diff, we store the user_id and sequential time to live (TTL) value. Setting TTL on a record enables us to reduce the amount of data stored. It also allows us to clean up records older than 30 days for security purposes. TTL functionality is provided by DynamoDB at no extra cost.<\/p>\n<p>On the admin side, once an admin requests user history several things happen:<\/p>\n<ol>\n<li>Current user state is read from the main user storage.<\/li>\n<li>Stored diffs for the user are loaded from the user history table, paginated by 30 states at a time.<\/li>\n<li>Starting with the current user state, a list of previous user states is generated by applying a JSON diff patch to each previous state.<\/li>\n<li>By comparing two adjacent states, we generate an HTML representation of the user changes. Generated HTML looks a lot like Github PR changes, with old values being highlighted in red, while new values being highlighted in green, so that the changes are immediately obvious.<\/li>\n<\/ol>\n<p>This is what the end-to-end architecture looks like:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-19478\" src=\"https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/02\/Architecture-diagram-of-Intercoms-User-History-Tool.png\" alt=\"\" width=\"1485\" height=\"654\" srcset=\"https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/02\/Architecture-diagram-of-Intercoms-User-History-Tool.png 1485w, https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/02\/Architecture-diagram-of-Intercoms-User-History-Tool-300x132.png 300w, https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/02\/Architecture-diagram-of-Intercoms-User-History-Tool-768x338.png 768w, https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/02\/Architecture-diagram-of-Intercoms-User-History-Tool-700x308.png 700w, https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/02\/Architecture-diagram-of-Intercoms-User-History-Tool-600x264.png 600w, https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/02\/Architecture-diagram-of-Intercoms-User-History-Tool-1400x617.png 1400w\" sizes=\"auto, (max-width: 1485px) 100vw, 1485px\" \/><\/p>\n<p>The flow of processing and storing the diff patches is serverless. <a href=\"https:\/\/aws.amazon.com\/lambda\/\" target=\"_blank\" rel=\"noopener noreferrer\">AWS Lamdba<\/a> takes care of everything required to run and scale code with high availability.<\/p>\n<h2 id=\"automating-repetitive-tasks\">Automating repetitive tasks<\/h2>\n<p>DynamoDB Streams were built <a href=\"https:\/\/docs.aws.amazon.com\/amazondynamodb\/latest\/developerguide\/Streams.html\" target=\"_blank\" rel=\"noopener noreferrer\">to capture table activity<\/a>, and their integrated <a href=\"https:\/\/docs.aws.amazon.com\/amazondynamodb\/latest\/developerguide\/Streams.Lambda.html\" target=\"_blank\" rel=\"noopener noreferrer\">AWS Lambda triggers<\/a>\u00a0easily enabled us to visualize updates in objects.<\/p>\n<blockquote class=\"pullquote-style-two\"><p>&#8220;Thinking simple and leveraging common technologies\u00a0is part of our engineering philosophy&#8221;<\/p><\/blockquote>\n<p>The UHT is now used internally by our Customer Support team and decreases the time spent on investigating problems related to the historical user states. Automating this manual repetitive task allows us to be more productive when solving our customers\u2019 issues and eliminates the risk of making mistakes in the process.<\/p>\n<p>Thinking simple and leveraging <a href=\"https:\/\/www.intercom.com\/blog\/videos\/build-boring-software\/\" target=\"_blank\" rel=\"noopener noreferrer\">common technologies<\/a> that we know and use frequently is part of our <a href=\"https:\/\/www.intercom.com\/blog\/run-less-software\/\" target=\"_blank\" rel=\"noopener noreferrer\">run less software<\/a> philosophy. We put it in practice by picking the right, reliable technology to enable us to build a simple, elegant, serverless solution which served our needs well.<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>DynamoDB Streams are built to capture table activity, and their integrated AWS Lambda triggers\u00a0easily enable us to visualize updates in objects, which was useful when building our User History Tool for our Customer Support team.<\/p>\n","protected":false},"author":263,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"category":[12898],"tags":[644,14199,14807],"coauthors":[574,1174],"class_list":["post-19474","post","type-post","status-publish","format-standard","hentry","category-engineering","tag-aws","tag-boring-technology","tag-dynamodb"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.3 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>How DynamoDB Helps to Visualize Changes in Updated Objects<\/title>\n<meta name=\"description\" content=\"DynamoDB Streams\u00a0easily enabled us to visualize updates in objects, which was useful when we built our User History Tool for our Customer Support team.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How we used DynamoDB Streams to visualize changes in frequently updated objects\" \/>\n<meta property=\"og:description\" content=\"DynamoDB Streams\u00a0easily enabled us to visualize updates in objects, which was useful when we built our User History Tool for our Customer Support team.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/\" \/>\n<meta property=\"og:site_name\" content=\"The Intercom Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/intercominc\" \/>\n<meta property=\"article:published_time\" content=\"2019-02-15T13:17:59+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2020-07-30T11:54:44+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/02\/Architecture-diagram-of-Intercoms-User-History-Tool.png\" \/>\n<meta name=\"author\" content=\"Maja Grubic, Sofia Tzima\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@princessmaja\" \/>\n<meta name=\"twitter:site\" content=\"@intercom\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Maja Grubic, Sofia Tzima\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/how-we-used-dynamodb-streams\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/how-we-used-dynamodb-streams\\\/\"},\"author\":{\"name\":\"Maja Grubic\",\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/#\\\/schema\\\/person\\\/06f90a7b2846e9285f622b0e22cbaaab\"},\"headline\":\"How we used DynamoDB Streams to visualize changes in frequently updated objects\",\"datePublished\":\"2019-02-15T13:17:59+00:00\",\"dateModified\":\"2020-07-30T11:54:44+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/how-we-used-dynamodb-streams\\\/\"},\"wordCount\":894,\"publisher\":{\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/how-we-used-dynamodb-streams\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/02\\\/Architecture-diagram-of-Intercoms-User-History-Tool.png\",\"keywords\":[\"AWS\",\"boring technology\",\"DynamoDB\"],\"articleSection\":[\"Engineering\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/how-we-used-dynamodb-streams\\\/\",\"url\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/how-we-used-dynamodb-streams\\\/\",\"name\":\"How DynamoDB Helps to Visualize Changes in Updated Objects\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/how-we-used-dynamodb-streams\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/how-we-used-dynamodb-streams\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/02\\\/Architecture-diagram-of-Intercoms-User-History-Tool.png\",\"datePublished\":\"2019-02-15T13:17:59+00:00\",\"dateModified\":\"2020-07-30T11:54:44+00:00\",\"description\":\"DynamoDB Streams\u00a0easily enabled us to visualize updates in objects, which was useful when we built our User History Tool for our Customer Support team.\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.intercom.com\\\/blog\\\/how-we-used-dynamodb-streams\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/how-we-used-dynamodb-streams\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/02\\\/Architecture-diagram-of-Intercoms-User-History-Tool.png\",\"contentUrl\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/02\\\/Architecture-diagram-of-Intercoms-User-History-Tool.png\",\"width\":1485,\"height\":654},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/\",\"name\":\"The Intercom Blog\",\"description\":\"Articles and Podcasts on Customer Service, AI and Automation, Product, and more\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/#organization\",\"name\":\"The Intercom Blog\",\"url\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/08\\\/Intercom-logo-sq-black-trans.png\",\"contentUrl\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/08\\\/Intercom-logo-sq-black-trans.png\",\"width\":1000,\"height\":1000,\"caption\":\"The Intercom Blog\"},\"image\":{\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/intercominc\",\"https:\\\/\\\/x.com\\\/intercom\",\"https:\\\/\\\/www.instagram.com\\\/intercom\\\/\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/2491343\",\"https:\\\/\\\/www.pinterest.ie\\\/intercom\\\/\",\"https:\\\/\\\/www.youtube.com\\\/channel\\\/UCJG0MvLP03kyzzAkD-w98aQ\",\"https:\\\/\\\/en.wikipedia.org\\\/wiki\\\/Intercom_(company)\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/#\\\/schema\\\/person\\\/06f90a7b2846e9285f622b0e22cbaaab\",\"name\":\"Maja Grubic\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/34421bcf4bd4368e0ec3c4da9e55366ada2e3acf99607b3a85a581e99d9b4707?s=96&d=mm&r=pg00e38c92f27f550fb7799032916ed080\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/34421bcf4bd4368e0ec3c4da9e55366ada2e3acf99607b3a85a581e99d9b4707?s=96&d=mm&r=pg\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/34421bcf4bd4368e0ec3c4da9e55366ada2e3acf99607b3a85a581e99d9b4707?s=96&d=mm&r=pg\",\"caption\":\"Maja Grubic\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/princessmaja\"],\"url\":\"https:\\\/\\\/www.intercom.com\\\/blog\\\/author\\\/princessmaja\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"How DynamoDB Helps to Visualize Changes in Updated Objects","description":"DynamoDB Streams\u00a0easily enabled us to visualize updates in objects, which was useful when we built our User History Tool for our Customer Support team.","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:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/","og_locale":"en_US","og_type":"article","og_title":"How we used DynamoDB Streams to visualize changes in frequently updated objects","og_description":"DynamoDB Streams\u00a0easily enabled us to visualize updates in objects, which was useful when we built our User History Tool for our Customer Support team.","og_url":"https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/","og_site_name":"The Intercom Blog","article_publisher":"https:\/\/www.facebook.com\/intercominc","article_published_time":"2019-02-15T13:17:59+00:00","article_modified_time":"2020-07-30T11:54:44+00:00","og_image":[{"url":"https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/02\/Architecture-diagram-of-Intercoms-User-History-Tool.png","type":"","width":"","height":""}],"author":"Maja Grubic, Sofia Tzima","twitter_card":"summary_large_image","twitter_creator":"@princessmaja","twitter_site":"@intercom","twitter_misc":{"Written by":"Maja Grubic, Sofia Tzima","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/#article","isPartOf":{"@id":"https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/"},"author":{"name":"Maja Grubic","@id":"https:\/\/www.intercom.com\/blog\/#\/schema\/person\/06f90a7b2846e9285f622b0e22cbaaab"},"headline":"How we used DynamoDB Streams to visualize changes in frequently updated objects","datePublished":"2019-02-15T13:17:59+00:00","dateModified":"2020-07-30T11:54:44+00:00","mainEntityOfPage":{"@id":"https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/"},"wordCount":894,"publisher":{"@id":"https:\/\/www.intercom.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/#primaryimage"},"thumbnailUrl":"https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/02\/Architecture-diagram-of-Intercoms-User-History-Tool.png","keywords":["AWS","boring technology","DynamoDB"],"articleSection":["Engineering"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/","url":"https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/","name":"How DynamoDB Helps to Visualize Changes in Updated Objects","isPartOf":{"@id":"https:\/\/www.intercom.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/#primaryimage"},"image":{"@id":"https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/#primaryimage"},"thumbnailUrl":"https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/02\/Architecture-diagram-of-Intercoms-User-History-Tool.png","datePublished":"2019-02-15T13:17:59+00:00","dateModified":"2020-07-30T11:54:44+00:00","description":"DynamoDB Streams\u00a0easily enabled us to visualize updates in objects, which was useful when we built our User History Tool for our Customer Support team.","inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.intercom.com\/blog\/how-we-used-dynamodb-streams\/#primaryimage","url":"https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/02\/Architecture-diagram-of-Intercoms-User-History-Tool.png","contentUrl":"https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/02\/Architecture-diagram-of-Intercoms-User-History-Tool.png","width":1485,"height":654},{"@type":"WebSite","@id":"https:\/\/www.intercom.com\/blog\/#website","url":"https:\/\/www.intercom.com\/blog\/","name":"The Intercom Blog","description":"Articles and Podcasts on Customer Service, AI and Automation, Product, and more","publisher":{"@id":"https:\/\/www.intercom.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.intercom.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.intercom.com\/blog\/#organization","name":"The Intercom Blog","url":"https:\/\/www.intercom.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.intercom.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/08\/Intercom-logo-sq-black-trans.png","contentUrl":"https:\/\/www.intercom.com\/blog\/wp-content\/uploads\/2019\/08\/Intercom-logo-sq-black-trans.png","width":1000,"height":1000,"caption":"The Intercom Blog"},"image":{"@id":"https:\/\/www.intercom.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/intercominc","https:\/\/x.com\/intercom","https:\/\/www.instagram.com\/intercom\/","https:\/\/www.linkedin.com\/company\/2491343","https:\/\/www.pinterest.ie\/intercom\/","https:\/\/www.youtube.com\/channel\/UCJG0MvLP03kyzzAkD-w98aQ","https:\/\/en.wikipedia.org\/wiki\/Intercom_(company)"]},{"@type":"Person","@id":"https:\/\/www.intercom.com\/blog\/#\/schema\/person\/06f90a7b2846e9285f622b0e22cbaaab","name":"Maja Grubic","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/34421bcf4bd4368e0ec3c4da9e55366ada2e3acf99607b3a85a581e99d9b4707?s=96&d=mm&r=pg00e38c92f27f550fb7799032916ed080","url":"https:\/\/secure.gravatar.com\/avatar\/34421bcf4bd4368e0ec3c4da9e55366ada2e3acf99607b3a85a581e99d9b4707?s=96&d=mm&r=pg","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/34421bcf4bd4368e0ec3c4da9e55366ada2e3acf99607b3a85a581e99d9b4707?s=96&d=mm&r=pg","caption":"Maja Grubic"},"sameAs":["https:\/\/x.com\/princessmaja"],"url":"https:\/\/www.intercom.com\/blog\/author\/princessmaja\/"}]}},"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.intercom.com\/blog\/wp-json\/wp\/v2\/posts\/19474","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.intercom.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.intercom.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.intercom.com\/blog\/wp-json\/wp\/v2\/users\/263"}],"replies":[{"embeddable":true,"href":"https:\/\/www.intercom.com\/blog\/wp-json\/wp\/v2\/comments?post=19474"}],"version-history":[{"count":0,"href":"https:\/\/www.intercom.com\/blog\/wp-json\/wp\/v2\/posts\/19474\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.intercom.com\/blog\/wp-json\/wp\/v2\/media?parent=19474"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.intercom.com\/blog\/wp-json\/wp\/v2\/category?post=19474"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.intercom.com\/blog\/wp-json\/wp\/v2\/tags?post=19474"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.intercom.com\/blog\/wp-json\/wp\/v2\/coauthors?post=19474"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}