[{"data":1,"prerenderedAt":900},["ShallowReactive",2],{"bootstrap":3,"blog-how-to-build-an-external-promotion-using-lambda-functions":277},{"navigation":4,"globalConfig":196,"forms":218},{"headerSections":5,"headerFooterLinks":67,"footer":76,"legalLinks":153,"socialLinks":176},[6,39,44,50,57,62],{"hasSubMenu":7,"title":8,"links":9,"columns":38},true,"Product",[10,20,26,32],{"showTag":11,"_modelApiKey":12,"text":13,"subtitle":14,"icon":15,"isExternal":11,"link":16,"openForm":11},false,"navigation_link","Universal checkout","Sell anywhere","globe-simple",{"_modelApiKey":17,"slug":18,"title":19},"page","universal-checkout","Universal Checkout",{"showTag":11,"_modelApiKey":12,"text":21,"subtitle":22,"icon":23,"isExternal":11,"link":24,"openForm":11},"Distributed OMS","Manage orders","package",{"_modelApiKey":17,"slug":25,"title":21},"distributed-oms",{"showTag":11,"_modelApiKey":12,"text":27,"subtitle":28,"icon":29,"isExternal":11,"link":30,"openForm":11},"Promotion engine","Automated discounts","seal-percent",{"_modelApiKey":17,"slug":31,"title":27},"promotion-engine",{"showTag":11,"_modelApiKey":12,"text":33,"subtitle":34,"icon":35,"isExternal":11,"link":36,"openForm":11},"POS system","Operate stores","devices",{"_modelApiKey":17,"slug":37,"title":33},"pos-system",2,{"hasSubMenu":11,"showTag":11,"_modelApiKey":12,"text":40,"isExternal":11,"link":41,"openForm":11,"title":40},"Pricing",{"_modelApiKey":42,"slug":43},"pricing_page","pricing",{"hasSubMenu":11,"showTag":11,"_modelApiKey":12,"text":45,"isExternal":11,"link":46,"openForm":11,"title":45},"Customers",{"_modelApiKey":47,"slug":48,"title":49},"customer_landing_page","customers","Customers page",{"hasSubMenu":11,"showTag":11,"_modelApiKey":12,"text":51,"isExternal":11,"link":52,"openForm":11,"form":54,"title":51},"Partners",{"_modelApiKey":17,"slug":53,"title":51},"partners",{"_modelApiKey":55,"slug":53,"name":56},"form","PartnersForm",{"hasSubMenu":11,"showTag":11,"_modelApiKey":12,"text":58,"label":59,"isExternal":7,"url":60,"openForm":11,"title":61},"Docs Hub","Learn more","https:\u002F\u002Fdocs.commercelayer.io\u002F","Docs",{"hasSubMenu":11,"showTag":11,"_modelApiKey":12,"text":63,"isExternal":11,"link":64,"openForm":11,"title":63},"Blog",{"_modelApiKey":65,"slug":66},"blog_landing_page","blog",[68,72],{"text":69,"link":70},"Sign in",{"_modelApiKey":12,"text":69,"isExternal":7,"url":71,"openForm":11},"https:\u002F\u002Fdashboard.commercelayer.io\u002Fsign_in",{"text":73,"link":74},"Sign up for free",{"_modelApiKey":12,"text":73,"isExternal":7,"url":75,"openForm":11},"https:\u002F\u002Fdashboard.commercelayer.io\u002Fsign_up",[77,89,109,132],{"hasSubMenu":7,"title":8,"links":78},[79,81,83,85,87],{"showTag":11,"_modelApiKey":12,"text":13,"subtitle":14,"icon":15,"isExternal":11,"link":80,"openForm":11},{"_modelApiKey":17,"slug":18,"title":19},{"showTag":11,"_modelApiKey":12,"text":21,"subtitle":22,"icon":23,"isExternal":11,"link":82,"openForm":11},{"_modelApiKey":17,"slug":25,"title":21},{"showTag":11,"_modelApiKey":12,"text":27,"subtitle":28,"icon":29,"isExternal":11,"link":84,"openForm":11},{"_modelApiKey":17,"slug":31,"title":27},{"showTag":11,"_modelApiKey":12,"text":33,"subtitle":34,"icon":35,"isExternal":11,"link":86,"openForm":11},{"_modelApiKey":17,"slug":37,"title":33},{"showTag":11,"_modelApiKey":12,"text":40,"isExternal":11,"link":88,"openForm":11},{"_modelApiKey":42,"slug":43},{"hasSubMenu":7,"title":90,"links":91},"Resources",[92,94,99,104,107],{"showTag":11,"_modelApiKey":12,"text":93,"isExternal":7,"url":60,"openForm":11},"Documentation",{"showTag":11,"_modelApiKey":12,"text":95,"label":95,"isExternal":11,"link":96,"openForm":11},"Agentic Commerce",{"_modelApiKey":17,"slug":97,"title":98},"agentic-commerce-whitepaper","Agentic Commerce Whitepaper",{"showTag":11,"_modelApiKey":12,"text":100,"isExternal":11,"link":101,"openForm":11},"Core concepts",{"_modelApiKey":17,"slug":102,"title":103},"headless-commerce","Headless commerce",{"showTag":11,"_modelApiKey":12,"text":105,"isExternal":7,"url":106,"openForm":11},"Changelog","https:\u002F\u002Fdocs.commercelayer.io\u002Fchangelog\u002F",{"showTag":11,"_modelApiKey":12,"text":63,"isExternal":11,"link":108,"openForm":11},{"_modelApiKey":65,"slug":66},{"hasSubMenu":7,"title":110,"links":111},"Company",[112,116,118,121,126],{"showTag":11,"_modelApiKey":12,"text":113,"isExternal":11,"link":114,"openForm":11},"Vision",{"_modelApiKey":17,"slug":115,"title":113},"vision",{"showTag":11,"_modelApiKey":12,"text":45,"isExternal":11,"link":117,"openForm":11},{"_modelApiKey":47,"slug":48,"title":49},{"showTag":11,"_modelApiKey":12,"text":51,"isExternal":11,"link":119,"openForm":11,"form":120},{"_modelApiKey":17,"slug":53,"title":51},{"_modelApiKey":55,"slug":53,"name":56},{"showTag":11,"_modelApiKey":12,"text":122,"isExternal":11,"link":123,"openForm":11},"Security",{"_modelApiKey":17,"slug":124,"title":125},"security","Security at Commerce Layer",{"showTag":11,"_modelApiKey":12,"text":127,"label":128,"isExternal":11,"openForm":7,"form":129},"Giveback","Giveback program",{"_modelApiKey":55,"slug":130,"name":131},"giveback","GivebackForm",{"hasSubMenu":7,"title":133,"links":134},"Get in touch",[135,140,145,149],{"showTag":11,"_modelApiKey":12,"text":136,"isExternal":11,"openForm":7,"form":137},"Contact",{"_modelApiKey":55,"slug":138,"name":139},"contact","ContactUsForm",{"showTag":11,"_modelApiKey":12,"text":141,"isExternal":11,"openForm":7,"form":142},"Book a demo",{"_modelApiKey":55,"slug":143,"name":144},"demo","RequestDemoForm",{"showTag":11,"_modelApiKey":12,"text":146,"label":147,"isExternal":7,"url":148,"openForm":11},"Support","Email support","mailto:support@commercelayer.io",{"showTag":11,"_modelApiKey":12,"text":150,"label":151,"isExternal":7,"url":152,"openForm":11},"Community","Discord community","https:\u002F\u002Fdiscord.gg\u002Fcommercelayer",{"hasSubMenu":7,"title":154,"links":155},"Legal",[156,161,165,169,173],{"showTag":11,"_modelApiKey":12,"text":157,"label":158,"isExternal":7,"url":159,"openForm":11,"htmlClassNames":160},"Terms","Terms and conditions","\u002Flegal-embed\u002Fterms-of-service","iubenda-nostyle no-brand iubenda-noiframe iubenda-embed",{"showTag":11,"_modelApiKey":12,"text":162,"label":163,"isExternal":7,"url":164,"openForm":11,"htmlClassNames":160},"Privacy","Privacy policy","\u002Flegal-embed\u002Fprivacy-policy",{"showTag":11,"_modelApiKey":12,"text":166,"label":167,"isExternal":7,"url":168,"openForm":11,"htmlClassNames":160},"Cookies","Cookie policy","\u002Flegal-embed\u002Fcookie-policy",{"showTag":11,"_modelApiKey":12,"text":170,"isExternal":7,"url":171,"openForm":11,"htmlClassNames":172},"Notice at collection","#","iubenda-cs-uspr-link",{"showTag":11,"_modelApiKey":12,"text":174,"isExternal":7,"url":171,"openForm":11,"htmlClassNames":175},"Privacy preferences","iubenda-cs-preferences-link",[177,180,184,188,192],{"title":178,"type":179,"url":152,"isExternal":7},"Discord","discord-logo",{"title":181,"type":182,"url":183,"isExternal":7},"Github","github","https:\u002F\u002Fgithub.com\u002Fcommercelayer",{"title":185,"type":186,"url":187,"isExternal":7},"Bluesky","bluesky-logo","https:\u002F\u002Fbsky.app\u002Fprofile\u002Fcommercelayer.io",{"title":189,"type":190,"url":191,"isExternal":7},"X","x-logo","https:\u002F\u002Fx.com\u002Fcommercelayer",{"title":193,"type":194,"url":195,"isExternal":7},"LinkedIn","linkedin","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fcommerce-layer",{"pagesWithoutCtaPanel":197,"ctaPanel":198,"notification":208,"showApiResponseTime":11,"newsletterSignup":216},[],{"title":199,"subtitle":200,"ctas":201},"Ready to get started?","Discover how we can help your business.",[202,205],{"_modelApiKey":203,"text":204,"isExternal":7,"url":75,"openForm":11},"link","Get started for free",{"_modelApiKey":203,"text":206,"isExternal":11,"openForm":7,"form":207},"Contact us",{"_modelApiKey":55,"slug":138,"name":139},{"showNotification":7,"link":209,"linkText":210,"text":215},{"_modelApiKey":203,"text":210,"isExternal":11,"link":211,"openForm":11},"Read article",{"_modelApiKey":212,"slug":213,"title":214},"blog_post","payment-api-redesigned","Introducing our new Payments API. For humans and agents.","\u003Cp>Introducing our new Payments API. For humans and agents.\u003C\u002Fp>\n",{"ctaText":217},"Subscribe",[219,225,229,236,240,246,249,253,261,262,267,272,274],{"_modelApiKey":55,"slug":220,"pardotFormEmbedCode":221,"formEmbedMinHeight":222,"successTitle":223,"successCopy":224},"agentic-commerce","https:\u002F\u002Fcommercelayer.fillout.com\u002Ft\u002F5w4Khwgxr6us?whitepaper=agentic",520,"Thank you for your request!","We’ll be in touch soon to arrange a date and time for your personalized demo.",{"_modelApiKey":55,"slug":226,"pardotFormEmbedCode":227,"formEmbedMinHeight":228,"successTitle":223,"successCopy":224},"shopify","https:\u002F\u002Fform.fillout.com\u002Ft\u002FgVnLSmKZwRus",850,{"_modelApiKey":55,"slug":130,"pardotFormEmbedCode":230,"formEmbedMinHeight":231,"displayTitle":232,"copy":233,"successTitle":234,"successCopy":235},"https:\u002F\u002Fform.fillout.com\u002Ft\u002FmVDF8aAiofus",500,"We give back.","\u003Cp>Part of Commerce Layer&#39;s company ethos is to empathize and support one another: co-workers, customers, and community. We want to do our part to help nonprofit organizations and small businesses who may feel adversely affected by the current global economic climate, and as a result, need in-kind support to reach their customers.\u003C\u002Fp>\n\n\u003Cp>If this general description matches your organization’s needs, please tell us your story by completing the following form so we can consider helping your cause.\u003C\u002Fp>\n","Thank you!","We will contact you soon to learn more about your story.",{"_modelApiKey":55,"slug":237,"pardotFormEmbedCode":227,"formEmbedMinHeight":228,"displayTitle":238,"copy":239},"book-an-onboarding-session","Book an onboarding session","\u003Cp>Schedule some time and our devs will help you set up a Commerce Layer + Sanity store.\u003C\u002Fp>\n",{"_modelApiKey":55,"slug":241,"pardotFormEmbedCode":242,"formEmbedMinHeight":243,"displayTitle":244,"successTitle":234,"successCopy":245},"download-our-whitepaper","https:\u002F\u002Fform.fillout.com\u002Ft\u002F5YPWmy8oT7us?whitepaper=ComposableCommerce",450,"Download our whitepaper.","Check your inbox. You should receive a download link soon.",{"_modelApiKey":55,"slug":247,"pardotFormEmbedCode":248,"formEmbedMinHeight":243,"displayTitle":244,"successTitle":234,"successCopy":245},"download-the-content-and-commerce-whitepaper","https:\u002F\u002Fform.fillout.com\u002Ft\u002F5YPWmy8oT7us?whitepaper=ContentAndCommerce",{"_modelApiKey":55,"slug":250,"pardotFormEmbedCode":251,"formEmbedMinHeight":243,"displayTitle":244,"copy":252,"successTitle":234,"successCopy":245},"whitepaper","https:\u002F\u002Fform.fillout.com\u002Ft\u002F5YPWmy8oT7us?whitepaper=Jamstack","\u003Cp>Commerce Layer is the perfect platform for building commerce on the Jamstack. Convert any design into pixel perfect HTML pages. Connect a best-of-breed headless CMS. Go serverless and forget the hassle of managing complex infrastructures.\u003C\u002Fp>\n",{"_modelApiKey":55,"slug":254,"pardotFormEmbedCode":255,"formEmbedMinHeight":256,"displayTitle":257,"copy":258,"successTitle":259,"successCopy":260},"contact-links","https:\u002F\u002Fform.fillout.com\u002Ft\u002FeWPyhHGHT7us",750,"Contact us.","\u003Cp>Tell us about your project. Our team will reach out to you for a personalized offer.\u003C\u002Fp>\n","Thanks for your message!","We’ll be in touch soon to discuss your project details.",{"_modelApiKey":55,"slug":138,"pardotFormEmbedCode":255,"formEmbedMinHeight":256,"displayTitle":257,"copy":258,"successTitle":259,"successCopy":260},{"_modelApiKey":55,"slug":263,"pardotFormEmbedCode":227,"formEmbedMinHeight":228,"displayTitle":264,"successTitle":265,"successCopy":266},"demo-embed","Want to see it in action? Request a demo.","Thanks for your request!","We’ll be in touch soon to arrange a date and time for your free live demo.",{"_modelApiKey":55,"slug":53,"pardotFormEmbedCode":268,"formEmbedMinHeight":228,"displayTitle":269,"copy":270,"successTitle":234,"successCopy":271},"https:\u002F\u002Fform.fillout.com\u002Ft\u002F9MTkDWMUSqus","Become our partner.","\u003Cp>Join us and get our guidance in helping your clients succeed. Our partners are our ambassadors. We are committed to your success as much as you are.\u003C\u002Fp>\n","We will contact you soon to discuss the details of our partnership.",{"_modelApiKey":55,"slug":273,"pardotFormEmbedCode":268,"formEmbedMinHeight":228,"displayTitle":269,"copy":270,"successTitle":234,"successCopy":271},"partners-embed",{"_modelApiKey":55,"slug":143,"pardotFormEmbedCode":227,"formEmbedMinHeight":228,"displayTitle":275,"copy":276,"successTitle":265,"successCopy":266},"Get a free live demo.","\u003Cp>Get a personalized demo and learn more about what Commerce Layer can do for your company.\u003C\u002Fp>\n",{"_modelApiKey":212,"link":278,"subtitle":283,"date":283,"author":284,"additionalAuthors":294,"icon":295,"abstract":296,"tag":297,"displayTitle":279,"cardRatio":300,"featured":11,"seoMetaTags":301,"layout":365,"blogPostCarousel":793,"_seoMetaTags":861,"authors":895},{"text":279,"isExternal":11,"url":280,"link":281},"How to build an external promotion using lambda functions.","",{"_modelApiKey":212,"slug":282},"how-to-build-an-external-promotion-using-lambda-functions","July 15, 2022",{"name":285,"picture":286},"Fabrizio Picca",{"url":287,"alt":288,"title":288,"width":289,"height":289,"format":290,"focalPoint":291,"customData":293},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1614875259-commercelayeravatarsquare113f26.png","Fabrizio Picca —      Partnerships Manager at Commerce Layer",1280,"png",{"x":292,"y":292},0.5,{},[],"gift","This tutorial will give you a little taste of how powerful Commerce Layer external promotions feature is, by showing an example where we use an AWS Lambda function to trigger a special discount on a customer's birthday.",{"color":298,"name":299},"gray","Tutorials","portrait",[302,306,310,313,317,320,323,327,331,335,338,342,346,350,354,357,361],{"attributes":303,"tag":304,"content":305},null,"title","How to Build an External Promotion Using Lambda Functions",{"attributes":307,"tag":309,"content":303},{"property":308,"content":305},"og:title","meta",{"attributes":311,"tag":309,"content":303},{"name":312,"content":305},"twitter:title",{"attributes":314,"tag":309,"content":303},{"name":315,"content":316},"description","This tutorial shows how to use the external promotions feature and an AWS Lambda function to trigger a special discount on a customer's birthday.",{"attributes":318,"tag":309,"content":303},{"property":319,"content":316},"og:description",{"attributes":321,"tag":309,"content":303},{"name":322,"content":316},"twitter:description",{"attributes":324,"tag":309,"content":303},{"property":325,"content":326},"og:image","https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657787122-how-to-build-an-external-promotion-using-lamba-functions.jpg?auto=format&fit=max&w=1200",{"attributes":328,"tag":309,"content":303},{"property":329,"content":330},"og:image:width","1200",{"attributes":332,"tag":309,"content":303},{"property":333,"content":334},"og:image:height","630",{"attributes":336,"tag":309,"content":303},{"name":337,"content":326},"twitter:image",{"attributes":339,"tag":309,"content":303},{"property":340,"content":341},"og:locale","en",{"attributes":343,"tag":309,"content":303},{"property":344,"content":345},"og:type","article",{"attributes":347,"tag":309,"content":303},{"property":348,"content":349},"og:site_name","Commerce Layer",{"attributes":351,"tag":309,"content":303},{"property":352,"content":353},"article:modified_time","2025-10-08T16:28:01Z",{"attributes":355,"tag":309,"content":303},{"property":356,"content":280},"article:publisher",{"attributes":358,"tag":309,"content":303},{"name":359,"content":360},"twitter:card","summary_large_image",{"attributes":362,"tag":309,"content":303},{"name":363,"content":364},"twitter:site","@commercelayer",[366,372,382,385,389,392,400,403,410,413,422,425,432,435,442,445,453,456,464,467,474,477,484,487,494,497,504,507,509,512,519,522,529,532,539,542,547,550,552,555,559,562,569,572,579,582,589,592,599,602,608,611,619,622,629,632,639,642,649,652,659,662,669,672,680,683,691,694,697,700,702,708,710,713,715,720,722,727,729,732,734,737,739,742,744,747,749,754,756,759,761,764,771,774,781,784,790],{"_modelApiKey":367,"copy":368,"listStyle":369,"darkTheme":11,"buttons":370,"color":371,"indent":7},"wysiwyg","\u003Cp>Commerce Layer is different from other ecommerce solutions because we&#39;re solely focused on building the best transactional engine possible. As part of that mission, we&#39;ve build our API to be extremely flexible, enabling developers to build what they need to build. In this article, we&#39;ll examine using \u003Ca target=\"_blank\" href=\"https:\u002F\u002Faws.amazon.com\u002Flambda\u002F\">AWS Lambda\u003C\u002Fa> functions, which shows exactly what we mean when we talk about flexibility.\u003C\u002Fp>\n\n\u003Cp>For example, for some of the core resources, Commerce Layer enables you to delegate the management to external services. Using \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fdocs.commercelayer.io\u002Fdevelopers\u002Fexternal-resources\">external resources\u003C\u002Fa>, it is possible to \u003Cstrong>use custom logic when working with prices, promotions, payment gateways, and tax calculators.\u003C\u002Fstrong>\u003C\u002Fp>\n\n\u003Cp>The below diagram explains how external resources are used by Commerce Layer:\u003C\u002Fp>\n","directional",[],"red",{"_modelApiKey":373,"border":11,"caption":280,"image":374,"size":381,"darkTheme":11,"rounded":11,"color":371},"image_block",{"url":375,"alt":376,"title":376,"width":377,"height":378,"format":379,"focalPoint":303,"customData":380},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657560477-external-promotions_no-padding.svg","Commerce Layer external promotions",1170,462,"svg",{},"standard",{"_modelApiKey":367,"copy":383,"listStyle":369,"darkTheme":11,"buttons":384,"color":371,"indent":11},"\u003Cp>The concept is pretty simple: when a resource configured as external is requested, Commerce Layer will trigger a \u003Ccode>POST\u003C\u002Fcode> call to the endpoint defined at the resource level. This endpoint can be a micro-service, a web interface of a legacy system, or an AWS Lambda. The result of the \u003Ccode>POST\u003C\u002Fcode> call will be used by Commerce Layer for the intended resource used (i.e. for external prices, the result of the \u003Ccode>POST\u003C\u002Fcode> request will be used as the actual price of an SKU).\u003C\u002Fp>\n",[],{"_modelApiKey":386,"icon":387,"copy":388,"color":371},"banner","info-bold","\u003Cp>The term \u003Cem>lambda\u003C\u002Fem> come from mathematics and it has been adopted in computer science as a synonym of &quot;anonymous function&quot;. Plenty of resources exist to dive deeper into the topic, starting with \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FAnonymous_function\">this Wikipedia article\u003C\u002Fa>.\u003C\u002Fp>\n",{"_modelApiKey":367,"copy":390,"listStyle":369,"darkTheme":11,"buttons":391,"color":371,"indent":11},"\u003Ch3>The goal: implementing a birthday promotion\u003C\u002Fh3>\n\n\u003Cp>The goal of this tutorial is to create an external promotion that triggers a special discount on a customer’s birthday. Let’s take a look at how a promotion might be calculated using an external system like an AWS Lambda function.\u003C\u002Fp>\n",[],{"_modelApiKey":393,"title":394,"preTitle":395,"linkText":396,"darkTheme":11,"link":397},"link_block","External promotions.","How-to","Read the guide",{"_modelApiKey":203,"text":398,"label":280,"subtitle":280,"icon":280,"isExternal":7,"url":399,"link":303,"anchor":280,"openForm":11,"form":303,"htmlClassNames":280},"Read more","https:\u002F\u002Fdocs.commercelayer.io\u002Fapi\u002Fexternal-resources\u002Fexternal-promotions",{"_modelApiKey":367,"copy":401,"listStyle":369,"darkTheme":11,"buttons":402,"color":371,"indent":11},"\u003Cp>To begin, we know that \u003Cstrong>promotions are calculated at the shopping cart, requiring a recalculation each time the cart is modified or when a promo code is entered successfully.\u003C\u002Fstrong> Upon a cart recalculation, the Commerce Layer promotional engine calculates any promos that might exist. Depending on activation rules, one or more promos will fire off and be added to the cart. If a promo is defined as external, Commerce Layer will execute a \u003Ccode>POST\u003C\u002Fcode> request to the endpoint specified as a promotion URL, sending the order in the payload of the request. The external system (in our case, the lambda) will elaborate on the context of the request, and provide a discount value back to Commerce Layer. Commerce Layer takes the response result and applies it to the current order.\u003C\u002Fp>\n\n\u003Cp>The execution flow is depicted in the sequence diagram:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":11,"caption":280,"image":404,"size":381,"darkTheme":11,"rounded":11,"color":371},{"url":405,"alt":406,"title":406,"width":407,"height":408,"format":379,"focalPoint":303,"customData":409},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657561510-birthday-external-promo_no-padding.svg","Birthday external promotion diagram",887,724,{},{"_modelApiKey":367,"copy":411,"listStyle":369,"darkTheme":11,"buttons":412,"color":371,"indent":11},"\u003Cp>To make this happen with Commerce Layer, and using the AWS lambda function as our external resource, we will do the following:\u003C\u002Fp>\n\n\u003Col>\n\u003Cli>Set up a fresh Commerce Layer environment.\u003C\u002Fli>\n\u003Cli>Set up the AWS environment.\u003C\u002Fli>\n\u003Cli>Create a lambda function containing the birthday promotion logic.\u003C\u002Fli>\n\u003Cli>Configure an external promotion in Commerce Layer.\u003C\u002Fli>\n\u003Cli>Set up the API proxy to expose the lambda.\u003C\u002Fli>\n\u003Cli>Wire everything together.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Ch3>1. Setting up the Commerce Layer environment\u003C\u002Fh3>\n\n\u003Cp>If you don’t have a Commerce Layer account, now is the right time to create one. You can create one for free (and it will \u003Cem>always\u003C\u002Fem> be free) \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fdashboard.commercelayer.io\u002Fsign_up\">here\u003C\u002Fa>. You don’t need a credit card. You just need to provide your first and last name, your email and a password.\u003C\u002Fp>\n\n\u003Cp>Now that you have an account, you can follow the \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fdocs.commercelayer.io\u002Fdevelopers\u002Fwelcome\u002Fonboarding-tutorial\">onboarding tutorial\u003C\u002Fa> that explains how to create an organization and seed it with test data. If you already have an account, you can use an existing organization, or create a new one. It’s up to you.\u003C\u002Fp>\n\n\u003Cp>Assuming everything goes smoothly, at the end of this step you will have:\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>A Commerce Layer account.\u003C\u002Fli>\n\u003Cli>An organization with some data.\u003C\u002Fli>\n\u003Cli>The \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fgithub.com\u002Fcommercelayer\u002Fcommercelayer-cli\">Commerce Layer CLI\u003C\u002Fa> installed on your computer, alongside with some of its plugins.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch3>2. Setting up the AWS environment\u003C\u002Fh3>\n\n\u003Cp>Setting up an AWS environment is free for testing\u002Fdevelopment purposes. You can set up your AWS account \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fportal.aws.amazon.com\u002Fbilling\u002Fsignup#\u002Fstart\u002Femail\">here\u003C\u002Fa>. You will need to provide a valid email address and an account name. AWS requires a credit card on file even if you will not pass the free tier limits. \u003Ca target=\"_blank\" href=\"https:\u002F\u002Faws.amazon.com\u002Ffree\u002F\">Here\u003C\u002Fa> you can find a comprehensive explanation of AWS’ Free Tier.\u003C\u002Fp>\n\n\u003Cp>There’s quite a lot you can configure when setting up your account, including the region that hosts your environment. The setup process is smooth and you should be presented with a screen like this once done:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":414,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":415,"alt":416,"title":416,"width":417,"height":418,"format":419,"focalPoint":420,"customData":421},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657880554-aws-console-home.jpg","AWS console home",1920,1234,"jpg",{"x":292,"y":292},{},{"_modelApiKey":367,"copy":423,"listStyle":369,"darkTheme":11,"buttons":424,"color":371,"indent":11},"\u003Cp>For this tutorial we are going to use three AWS Services:\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>The \u003Ca target=\"_blank\" href=\"https:\u002F\u002Faws.amazon.com\u002Flambda\u002F\">AWS Lambda\u003C\u002Fa> environment — the runtime that will execute our custom promotion code as we will see later in the tutorial.\u003C\u002Fli>\n\u003Cli>An instance (table) of \u003Ca target=\"_blank\" href=\"https:\u002F\u002Faws.amazon.com\u002Fdynamodb\u002F\">AWS DynamoDB\u003C\u002Fa> — the \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FNoSQL\">NoSQL\u003C\u002Fa> database that will be used to store all customer birthday information as we will see later in the tutorial.\u003C\u002Fli>\n\u003Cli>The \u003Ca target=\"_blank\" href=\"https:\u002F\u002Faws.amazon.com\u002Fapi-gateway\u002F\">AWS API Gateway\u003C\u002Fa> — this is how we will expose our lambda on the web. Basically, the service allows for the creation of custom endpoints that can be further connected to other AWS services (such as Lambda).\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch3>3. Setting up the DynamoDB for customers’ dates of birth (DOBs)\u003C\u002Fh3>\n\n\u003Cp>You can access the \u003Cstrong>DynamoDB\u003C\u002Fstrong> dashboard from the AWS console. Type &quot;DynamoDB&quot; in the search bar and select the service:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":426,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":427,"alt":428,"title":428,"width":417,"height":429,"format":419,"focalPoint":430,"customData":431},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657880957-aws-console-search.jpg","AWS console search",891,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":433,"listStyle":369,"darkTheme":11,"buttons":434,"color":371,"indent":11},"\u003Cp>Select \u003Cem>Table\u003C\u002Fem> and then \u003Cem>Create table\u003C\u002Fem> as shown below:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":436,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":437,"alt":438,"title":438,"width":417,"height":439,"format":419,"focalPoint":440,"customData":441},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657881236-dynamodb-table-create.jpg","DynamoDB table create",616,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":443,"listStyle":369,"darkTheme":11,"buttons":444,"color":371,"indent":11},"\u003Cp>We need now to create the \u003Ccode>CUSTOMER_DOB\u003C\u002Fcode> table as shown in the panel below:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":446,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":447,"alt":448,"title":448,"width":449,"height":450,"format":419,"focalPoint":451,"customData":452},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657881529-dynamodb-creation-panel.jpg","DynamoDB creation panel",1600,1539,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":454,"listStyle":369,"darkTheme":11,"buttons":455,"color":371,"indent":11},"\u003Cp>\u003Ccode>EMAIL\u003C\u002Fcode> will be our partition key, the primary key that will be used to fetch data in the table. Keep in mind that since DynamoDB is a schema-less NoSQL database, we are not going to create any columns. Attributes will be added starting with the first insertions. Let’s see how that works.\u003C\u002Fp>\n\n\u003Cp>From the DynamoDB \u003Cem>Tables\u003C\u002Fem> page, select the newly created table, \u003Ccode>CUSTOMER_DOB\u003C\u002Fcode>:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":457,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":458,"alt":459,"title":459,"width":460,"height":461,"format":290,"focalPoint":462,"customData":463},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657632238-dynamodb-tables.png","DynamoDB tables",2000,553,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":465,"listStyle":369,"darkTheme":11,"buttons":466,"color":371,"indent":11},"\u003Cp>Then go to \u003Cem>Explore items\u003C\u002Fem> and press \u003Cem>Create item\u003C\u002Fem>:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":468,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":469,"alt":470,"title":470,"width":460,"height":471,"format":290,"focalPoint":472,"customData":473},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657632537-dynamodb-create-item.png","DynamoDB create item",586,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":475,"listStyle":369,"darkTheme":11,"buttons":476,"color":371,"indent":11},"\u003Cp>At this point, we need to add a new object to the table, as shown below:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":478,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":479,"alt":480,"title":480,"width":460,"height":481,"format":290,"focalPoint":482,"customData":483},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657632662-dynamodb-item-editor.png","DynamoDB item editor",427,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":485,"listStyle":369,"darkTheme":11,"buttons":486,"color":371,"indent":11},"\u003Cp>Let’s add three new attributes of our object:\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>\u003Ccode>DOB_D\u003C\u002Fcode> — The day of the month (1-31). Use \u003Cem>Number\u003C\u002Fem> as the attribute type.\u003C\u002Fli>\n\u003Cli>\u003Ccode>DOB_M\u003C\u002Fcode> — The month of the year (1-12). Use \u003Cem>Number\u003C\u002Fem> as the attribute type.\u003C\u002Fli>\n\u003Cli>\u003Ccode>DOB_Y\u003C\u002Fcode> — The year (e.g. &quot;20222). Use \u003Cem>Number\u003C\u002Fem> as the attribute type.\u003C\u002Fli>\n\u003C\u002Ful>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":488,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":489,"alt":490,"title":490,"width":460,"height":491,"format":290,"focalPoint":492,"customData":493},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657632974-dynamodb-item-attributes.png","DynamoDB item attributes",580,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":495,"listStyle":369,"darkTheme":11,"buttons":496,"color":371,"indent":11},"\u003Cp>Once the form is filled with the necessary values, click \u003Cem>Create item\u003C\u002Fem> and a new item will be added to the database:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":498,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":499,"alt":500,"title":500,"width":460,"height":501,"format":290,"focalPoint":502,"customData":503},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657633069-dynamodb-item-created.png","DynamoDB item created",700,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":505,"listStyle":369,"darkTheme":11,"buttons":506,"color":371,"indent":11},"\u003Cp>For the purpose of this demo, our DB setup is now complete. Let’s look at the lambda function.\u003C\u002Fp>\n\n\u003Ch3>4. Building the birthday promotion lambda\u003C\u002Fh3>\n\n\u003Cp>\u003Cstrong>AWS Lambdas\u003C\u002Fstrong> can be used as a way to implement an external service that can be used as an external resource by Commerce Layer. Let’s start by explaining AWS Lambdas. \u003C\u002Fp>\n",[],{"_modelApiKey":386,"icon":387,"copy":508,"color":371},"\u003Cp>AWS Lambda is an event-driven, serverless computing platform provided by Amazon as a part of Amazon Web Services. It is a computing service that runs code in response to events and automatically manages the computing resources required by that code. (source: \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FAWS_Lambda\">Wikipedia\u003C\u002Fa>)\u003C\u002Fp>\n",{"_modelApiKey":367,"copy":510,"listStyle":369,"darkTheme":11,"buttons":511,"color":371,"indent":11},"\u003Cp>Let’s unpack this definition, remembering the sequence we showed at the very beginning of this article:\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>\u003Cstrong>Event-driven\u003C\u002Fstrong> — AWS Lambdas &quot;react&quot; to events. This means that our function is triggered \u003Cem>only\u003C\u002Fem> when a specific event occurs, and will stay active only for the time needed to perform the calculation. This is different from micro-services, for example, which are &quot;always on&quot;. Because of this, AWS bills customers on execution time and not on resources used by the code. In our example, the event is the \u003Ccode>POST\u003C\u002Fcode> request coming from Commerce Layer targeted to an API gateway proxy (our external resource endpoint) that eventually triggers the lambda.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Serverless\u003C\u002Fstrong> — AWS Lambdas don’t require any provisioning in terms of containers or virtual\u002Fphysical resources. Resources are automatically managed by the runtime platform.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Code\u003C\u002Fstrong> — Yes, there’s code. In our case, the code will be the logic of the external promotion we want to run. This is the &quot;core&quot; of our lambda function.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Now that we have the terminology down, let’s create a function on AWS Lambda. From the AWS home page click \u003Cem>Lambda\u003C\u002Fem> and you will be directed to your functions:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":513,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":514,"alt":515,"title":515,"width":460,"height":516,"format":290,"focalPoint":517,"customData":518},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657633851-aws-lambda-functions.png","AWS Lambda functions",487,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":520,"listStyle":369,"darkTheme":11,"buttons":521,"color":371,"indent":11},"\u003Cp>Click the orange button \u003Cem>Create function\u003C\u002Fem>.\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":523,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":524,"alt":525,"title":525,"width":460,"height":526,"format":290,"focalPoint":527,"customData":528},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657634001-aws-lambda-create-function.png","AWS lambda create function",973,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":530,"listStyle":369,"darkTheme":11,"buttons":531,"color":371,"indent":11},"\u003Cp>Select \u003Cem>Author from scratch\u003C\u002Fem> and give your function a name. In our case, we are naming it &quot;BirthdayPromoTutorial&quot;.\u003C\u002Fp>\n\n\u003Cp>AWS Lambda allows different runtimes for your function, meaning that you can write code in your preferred language. We&#39;ll be using \u003Cstrong>JavaScript\u003C\u002Fstrong> and the \u003Cstrong>Node.js 14.x\u003C\u002Fstrong> runtime.\u003C\u002Fp>\n\n\u003Cp>For the purposes of the tutorial, you don’t need to deviate from the default permissions settings. After you click the \u003Cem>Create function\u003C\u002Fem> button, you will see Amazon’s IDE where you can begin writing the code for the lambda function: \u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":533,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":534,"alt":535,"title":535,"width":460,"height":536,"format":290,"focalPoint":537,"customData":538},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657634340-aws-lambda-code.png","AWS lambda code",1084,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":540,"listStyle":369,"darkTheme":11,"buttons":541,"color":371,"indent":11},"\u003Cp>The basic function template receives an \u003Ccode>event\u003C\u002Fcode> as input, and then returns a \u003Ccode>response\u003C\u002Fcode>:\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>The \u003Ccode>event\u003C\u002Fcode> contains the data from Commerce Layer (in our case, the customer’s email address which we specified as the primary key above).\u003C\u002Fli>\n\u003Cli>The \u003Ccode>response\u003C\u002Fcode> is the &quot;outcome&quot; (which is the benefit the customer will get as a result of the birthday promotion).\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Let’s take a closer look to these two elements in more details.\u003C\u002Fp>\n\n\u003Ch4>The event\u003C\u002Fh4>\n\n\u003Cp>As we mentioned above, the event provides context as the payload of the \u003Ccode>POST\u003C\u002Fcode> request done to the external promotion endpoint. Our documentation for \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fdocs.commercelayer.io\u002Fdevelopers\u002Fexternal-resources\u002Fexternal-promotions\">external promotions\u003C\u002Fa> specifies the structure of the payload:\u003C\u002Fp>\n",[],{"_modelApiKey":543,"code":544,"showLineNumbers":11,"highlightLines":280,"language":545,"size":546},"code_block","{\n  \"data\": {\n    \"id\": \"wBXVhKzrnq\",\n    \"type\": \"orders\",\n    \"links\": { ... },\n    \"attributes\": { ... },\n    \"relationships\": { ... },\n    \"meta\": { ... }\n  },\n  \"included\": [\n    {\n      \"id\": \"DvlGRmhdgX\",\n      \"type\": \"markets\",\n      \"links\": { ... },\n      \"attributes\": { ... },\n      \"relationships\": { ... },\n      \"meta\": { ... }\n    },\n    {\n      \"id\": \"kdPgtRXOKL\",\n      \"type\": \"line_items\",\n      \"links\": { ... },\n      \"attributes\": { ... },\n      \"relationships\": { ... },\n      \"meta\": { ... }\n    },\n    {\n      \"id\": \"XGZwpOSrWL\",\n      \"type\": \"skus\",\n      \"links\": { ... },\n      \"attributes\": { ... },\n      \"relationships\": { ... },\n      \"meta\": { ... }\n    }\n    {\n      \"id\": \"BgnguJvXmb\",\n      \"type\": \"addresses\",\n      \"links\": { ... },\n      \"attributes\": { ... },\n      \"relationships\": { ... },\n      \"meta\": { ... }\n    },\n    {\n      \"id\": \"AlrkugwyVW\",\n      \"type\": \"addresses\",\n      \"links\": { ... },\n      \"attributes\": { ... },\n      \"relationships\": { ... },\n      \"meta\": { ... }\n    },\n    { ... }\n  ]\n}","json","Standard",{"_modelApiKey":367,"copy":548,"listStyle":369,"darkTheme":11,"buttons":549,"color":371,"indent":11},"\u003Cp>As you can see, the the customer identifier (aka the primary key, which is the user’s email) can be found in the \u003Ccode>data.attributes.customer_email\u003C\u002Fcode> property. This identifier will be used by our lambda function to fetch the customer date of birth from the DynamoDB we created above.\u003C\u002Fp>\n\n\u003Ch4>The response\u003C\u002Fh4>\n\n\u003Cp>The response is what gets returned to Commerce Layer. From the \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fdocs.commercelayer.io\u002Fdevelopers\u002Fexternal-resources\u002Fexternal-promotions#response\">documentation\u003C\u002Fa> we expect something like the following:\u003C\u002Fp>\n",[],{"_modelApiKey":543,"code":551,"showLineNumbers":11,"highlightLines":280,"language":545,"size":546},"{\n  \"success\": true,\n  \"data\": {\n    \"name\": \"My External Promotion\",\n    \"discount_cents\": 1500,\n    \"metadata\": {\n      \"foo\": \"bar\"\n    }\n  }\n}",{"_modelApiKey":367,"copy":553,"listStyle":369,"darkTheme":11,"buttons":554,"color":371,"indent":11},"\u003Cp>As you can see, \u003Ccode>data.discount_cents\u003C\u002Fcode> contains the discount the customer will get as part of the birthday promotion.\u003C\u002Fp>\n\n\u003Ch4>The lambda code\u003C\u002Fh4>\n\n\u003Cp>Now that we have introduced all the pieces of our lambda, let’s look at the sample code we are going to use to calculate the special discount:\u003C\u002Fp>\n",[],{"_modelApiKey":543,"code":556,"showLineNumbers":11,"highlightLines":280,"language":557,"size":558},"const AWS = require(\"aws-sdk\");\n\nexports.handler = async (event) => {\n  let body, statusCode;\n  const headers = {\n    \"Content-Type\": \"application\u002Fjson\",\n  };\n\n  try {\n    const customer = JSON.parse(event.body).data.attributes.customer_email;\n    const customer_dob = await getCustomerDob(customer);\n\n    if (customer_dob) {\n      console.log(customer_dob);\n      if (isBirthdayToday(customer_dob)) {\n        statusCode = 200;\n        body = {\n          success: true,\n          data: {\n            name: \"Birthday Promo\",\n            discount_cents: 1500,\n          },\n        };\n      }\n    } else {\n      statusCode = 404;\n      body = {\n        success: \"false\",\n        error: {\n          code: \"error 2\",\n          message: \"customer not found!\",\n        },\n      };\n    }\n  } catch (err) {\n    statusCode = 500;\n    body = {\n      success: \"false\",\n      error: {\n        code: \"error 1\",\n        message: err.message,\n      },\n    };\n  } finally {\n    body = JSON.stringify(body);\n  }\n\n  return {\n    statusCode,\n    headers,\n    body,\n  };\n};\n\n\u002F\u002F Get Customer Date of Birth from a Dynamo DB\n\nasync function getCustomerDob(customer) {\n  const dynamo = new AWS.DynamoDB.DocumentClient();\n  let customer_dob;\n  customer_dob = await dynamo\n    .get({\n      TableName: \"CUSTOMER_DOB\",\n      Key: {\n        EMAIL: customer,\n      },\n    })\n    .promise();\n\n  return customer_dob.Item;\n}\n\n\u002F\u002F Simple function that says if today date match a date of Birth (dob)\n\nfunction isBirthdayToday(dob) {\n  if (\n    dob.DOB_M == new Date().getMonth() + 1 &&\n    dob.DOB_D == new Date().getDate()\n  )\n    return true;\n  else return false;\n}","javascript","Wide",{"_modelApiKey":367,"copy":560,"listStyle":369,"darkTheme":11,"buttons":561,"color":371,"indent":11},"\u003Cp>The above code does the following:\u003C\u002Fp>\n\n\u003Col>\n\u003Cli>Extracts a customer’s email from an event.\u003C\u002Fli>\n\u003Cli>Fetches the customer DOB from the database (note: if no customer is found, the function returns a status code \u003Ccode>400\u003C\u002Fcode> with \u003Ccode>success: false\u003C\u002Fcode> and \u003Ccode>message: customer not found\u003C\u002Fcode>).\u003C\u002Fli>\n\u003Cli>Compares the current date with customer DOB.\u003C\u002Fli>\n\u003Cli>If a match exists, the function returns both a status code \u003Ccode>200\u003C\u002Fcode> with \u003Ccode>success: true\u003C\u002Fcode>, and the promotion value in the format expected by Commerce Layer’s external promotion feature.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>The entire function body is surrounded by \u003Ccode>try \u002F catch \u002F finally\u003C\u002Fcode> to capture any system exceptions and return appropriate error codes (\u003Ccode>500\u003C\u002Fcode> in our case for system exceptions).\u003C\u002Fp>\n\n\u003Cp>Please note that the code shown above is for example’s sake. Undoubtedly, there are better ways to implement the promotion logic. Feel free to share your code if you like!\u003C\u002Fp>\n\n\u003Cp>That said, let’s paste this code in the IDE and deploy:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":563,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":564,"alt":565,"title":565,"width":460,"height":566,"format":290,"focalPoint":567,"customData":568},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657635952-aws-ide-past-code.png","AWS IDE",1131,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":570,"listStyle":369,"darkTheme":11,"buttons":571,"color":371,"indent":11},"\u003Cp>We also need to grant the lambda function proper access to the DynamoDB, which we can do by using the \u003Ca target=\"_blank\" href=\"https:\u002F\u002Faws.amazon.com\u002Fiam\u002F\">AWS Identity and Access Management\u003C\u002Fa> tool (IAM). From the configuration panel of the lambda, select \u003Cem>Permissions\u003C\u002Fem>, and click the \u003Cem>Execution role\u003C\u002Fem> that was created by default:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":573,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":574,"alt":575,"title":575,"width":460,"height":576,"format":290,"focalPoint":577,"customData":578},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657636362-aws-conf-permissions.png","AWS config permissions",1180,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":580,"listStyle":369,"darkTheme":11,"buttons":581,"color":371,"indent":11},"\u003Cp>This opens the IAM for the selected role. Make sure that the \u003Ccode>AmazonDynamoDBReadOnlyAccess\u003C\u002Fcode> is present in the \u003Cem>Permission policies\u003C\u002Fem> lists. If it isn’t, add it by clicking on \u003Cem>Add Permissions\u003C\u002Fem> and searching for it in the dropdown:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":583,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":584,"alt":585,"title":585,"width":460,"height":586,"format":290,"focalPoint":587,"customData":588},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657636587-iam-permissions-policies.png","IAM permissions policies",936,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":590,"listStyle":369,"darkTheme":11,"buttons":591,"color":371,"indent":11},"\u003Cp>Make also sure to always hit the \u003Cem>Deploy\u003C\u002Fem> button in the AWS Lambda IDE, otherwise you won’t see the changes to your code.\u003C\u002Fp>\n\n\u003Cp>Our lambda setup is complete, and now it’s time to move to the next step.\u003C\u002Fp>\n\n\u003Ch3>Setting up the API gateway\u003C\u002Fh3>\n\n\u003Cp>Now that our lambda is ready, we can expose it on the cloud by using the \u003Cstrong>Amazon API Gateway\u003C\u002Fstrong> service. There are different ways to set up a new API gateway, for this tutorial we’ll start from the AWS Lambda panel. As mentioned in the previous section, a Lambda function is triggered by an external event so you will need to click on \u003Cem>Add trigger\u003C\u002Fem>:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":593,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":594,"alt":595,"title":595,"width":460,"height":596,"format":290,"focalPoint":597,"customData":598},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657636805-add-trigger.png","AWS panel add trigger",638,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":600,"listStyle":369,"darkTheme":11,"buttons":601,"color":371,"indent":11},"\u003Cp>In the \u003Cem>Add trigger\u003C\u002Fem> page, select the \u003Cem>API Gateway\u003C\u002Fem> option for \u003Cem>Trigger configuration\u003C\u002Fem>:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":603,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":604,"alt":605,"title":605,"width":460,"height":536,"format":290,"focalPoint":606,"customData":607},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657637019-trigger-configuration.png","AWS Trigger configuration",{"x":292,"y":292},{},{"_modelApiKey":367,"copy":609,"listStyle":369,"darkTheme":11,"buttons":610,"color":371,"indent":11},"\u003Cp>Once you select the \u003Cem>API Gateway\u003C\u002Fem> option, some other configuration options will appear. Select \u003Cem>Create an API\u003C\u002Fem> in the API drop down, select \u003Cem>REST API\u003C\u002Fem>, and \u003Cem>Open\u003C\u002Fem> for the security options (please note that this is not the recommended way to configure a production service, but for the sake of this tutorial it’ll be ok).\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":612,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":613,"alt":614,"title":614,"width":615,"height":616,"format":290,"focalPoint":617,"customData":618},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657637210-trigger-config-options.png","API gateway trigger config options",1674,1340,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":620,"listStyle":369,"darkTheme":11,"buttons":621,"color":371,"indent":11},"\u003Cp>All you need to do now is press \u003Cem>Add\u003C\u002Fem> and your API Gateway is set. You will see \u003Cem>API Gateway\u003C\u002Fem> in the triggers section of the \u003Cem>Function overview\u003C\u002Fem> panel:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":623,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":624,"alt":625,"title":625,"width":460,"height":626,"format":290,"focalPoint":627,"customData":628},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657637537-api-gateway-function-overview.png","API Gateway function overview",725,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":630,"listStyle":369,"darkTheme":11,"buttons":631,"color":371,"indent":11},"\u003Cp>Now, let’s take a closer look at what we created in the AWS API Gateway page. Go to the AWS home console and select \u003Cem>API Gateway\u003C\u002Fem>:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":633,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":634,"alt":635,"title":635,"width":460,"height":636,"format":290,"focalPoint":637,"customData":638},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657637730-console-home-select-api-gateway.png","Console home select API gateway",620,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":640,"listStyle":369,"darkTheme":11,"buttons":641,"color":371,"indent":11},"\u003Cp>The screen below will appear. As you can see a new API has been deployed as a result of the previous steps:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":643,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":644,"alt":645,"title":645,"width":460,"height":646,"format":290,"focalPoint":647,"customData":648},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657637822-api-gateway-api-deployed.png","API deployed",333,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":650,"listStyle":369,"darkTheme":11,"buttons":651,"color":371,"indent":11},"\u003Cp>Let’s open the \u003Cem>BirthdayPromoTutorial-API\u003C\u002Fem> to see how things are set up by clicking on the API name. From the panel below, we can see that a resource has been created for our endpoint (\u003Ccode>\u002FBirthdayPromoTutorial\u003C\u002Fcode>) and this resource will have all the HTTP methods behaving the same. This means it will forward the payload to our lambda function, as the handy generated diagram shows:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":653,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":654,"alt":655,"title":655,"width":460,"height":656,"format":290,"focalPoint":657,"customData":658},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657637995-aws-method-execution-diagram.png","Method execution diagram",958,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":660,"listStyle":369,"darkTheme":11,"buttons":661,"color":371,"indent":11},"\u003Cp>This API Gateway setup is called \u003Cem>Proxy\u003C\u002Fem>, meaning that it will transparently forward the payload to the consumer (our lambda). The lambda itself handles requests and responses, as we saw in the previous section. Now that our API is set up, we just need to deploy and test it. From the \u003Cem>Actions\u003C\u002Fem> dropdown, you can see in the API panel, select \u003Cem>Deploy API\u003C\u002Fem>:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":663,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":664,"alt":665,"title":665,"width":460,"height":666,"format":290,"focalPoint":667,"customData":668},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657638185-actions-deploy-api.png","Actions - deploy API",967,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":670,"listStyle":369,"darkTheme":11,"buttons":671,"color":371,"indent":11},"\u003Cp>Select &quot;default&quot; as you deployment stage. This is where you can manage different stages for you API such as, production, development and staging:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":673,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":674,"alt":675,"title":675,"width":676,"height":677,"format":290,"focalPoint":678,"customData":679},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657638339-default-deployment-stage.png","Default deployment stage",1212,646,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":681,"listStyle":369,"darkTheme":11,"buttons":682,"color":371,"indent":11},"\u003Cp>And, there you go — our API is deployed and ready to accept requests:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":684,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":685,"alt":686,"title":687,"width":460,"height":688,"format":290,"focalPoint":689,"customData":690},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657638423-api-ready.png","API ready","APi ready",1175,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":692,"listStyle":369,"darkTheme":11,"buttons":693,"color":371,"indent":11},"\u003Cp>As indicated by the screen above, the base URL of our API gateway is:\u003C\u002Fp>\n",[],{"_modelApiKey":543,"code":695,"showLineNumbers":11,"highlightLines":280,"language":696,"size":558},"https:\u002F\u002F16xy12aiwl.execute-api.eu-south-1.amazonaws.com\u002Fdefault","shell",{"_modelApiKey":367,"copy":698,"listStyle":369,"darkTheme":11,"buttons":699,"color":371,"indent":11},"\u003Cp>And the \u003Ccode>\u002FBirthdayPromoTutorial\u003C\u002Fcode> endpoint can be reached at:\u003C\u002Fp>\n",[],{"_modelApiKey":543,"code":701,"showLineNumbers":11,"highlightLines":280,"language":696,"size":558},"https:\u002F\u002F16xy12aiwl.execute-api.eu-south-1.amazonaws.com\u002Fdefault\u002FBirthdayPromoTutorial",{"_modelApiKey":367,"copy":703,"listStyle":369,"darkTheme":11,"buttons":704,"color":371,"indent":11},"\u003Cp>Remember that when you are working inside your AWS instance, URLs are newly generated. This means that your actual URLs will be different from the examples here.\u003C\u002Fp>\n\n\u003Cp>Let&#39;s do a do a quick smoke test using \u003Cem>curl\u003C\u002Fem>:\u003C\u002Fp>\n",[705],{"darkTheme":11,"alignment":706,"buttons":707},"default",[],{"_modelApiKey":543,"code":709,"showLineNumbers":11,"highlightLines":280,"language":696,"size":558},"curl --location --request POST \\\n  'https:\u002F\u002F16xy12aiwl.execute-api.eu-south-1.amazonaws.com\u002Fdefault\u002FBirthdayPromoTutorial' \\\n  --header 'Content-Type: application\u002Fjson' \\\n  --data-raw '{\n    \"data\": \"just testing!\"\n  }'",{"_modelApiKey":367,"copy":711,"listStyle":369,"darkTheme":11,"buttons":712,"color":371,"indent":11},"\u003Cp>As you can see, our API is up and running! We receive an error response since we sent some &quot;dummy&quot; data:\u003C\u002Fp>\n",[],{"_modelApiKey":543,"code":714,"showLineNumbers":11,"highlightLines":280,"language":545,"size":558},"{\n  \"success\": \"false\",\n  \"error\": {\n    \"code\": \"error 1\",\n    \"message\": \"Cannot read property 'attributes' of undefined\"\n  }\n}",{"_modelApiKey":367,"copy":716,"listStyle":369,"darkTheme":11,"buttons":717,"color":371,"indent":11},"\u003Cp>So, in the next step, we will configure Commerce Layer to send real data to our endpoint, and we’ll see the birthday promo in action.\u003C\u002Fp>\n\n\u003Ch3>6. Wiring everything together\u003C\u002Fh3>\n\n\u003Cp>The last step is connecting Commerce Layer to our newly created AWS Lambda environment. \u003C\u002Fp>\n\n\u003Cp>We need to create and configure an external promotion. To do that we&#39;ll use the Commerce Layer CLI (the same result can be achieved also from our admin dashboard UI directly).\u003C\u002Fp>\n\n\u003Cp>\u003Ca target=\"_blank\" href=\"https:\u002F\u002Fgithub.com\u002Fcommercelayer\u002Fcommercelayer-cli\">Our CLI\u003C\u002Fa> helps you to manage your Commerce Layer applications, including promotions, right from the terminal. If you followed the onboarding tutorial as mentioned at the begininng of the article you should already have it installed. Otherwise, you can do it using you favorite package manager:\u003C\u002Fp>\n",[718],{"darkTheme":11,"alignment":706,"buttons":719},[],{"_modelApiKey":543,"code":721,"showLineNumbers":11,"highlightLines":280,"language":696,"size":546},"\u002F\u002Fnpm\nnpm install -g @commercelayer\u002Fcli\n\n\u002F\u002Fyarn\nyarn global add @commercelayer\u002Fcli",{"_modelApiKey":367,"copy":723,"listStyle":369,"darkTheme":11,"buttons":724,"color":371,"indent":11},"\u003Cp>Installing the CLI provides access to the \u003Ccode>commercelayer\u003C\u002Fcode> command that can also be accessed with the \u003Ccode>cl\u003C\u002Fcode> alias.\u003C\u002Fp>\n\n\u003Cp>Follow \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fdocs.commercelayer.io\u002Fdevelopers\u002Fwelcome\u002Fmanual-configuration#create-an-application\">this guide\u003C\u002Fa> in the documentation to create an \u003Cem>integration\u003C\u002Fem> application and save the provided \u003Cstrong>client ID\u003C\u002Fstrong>, \u003Cstrong>client secret\u003C\u002Fstrong>, and \u003Cstrong>base endpoint\u003C\u002Fstrong> credentials. Then use those credentials to log into your application like so:\u003C\u002Fp>\n",[725],{"darkTheme":11,"alignment":706,"buttons":726},[],{"_modelApiKey":543,"code":728,"showLineNumbers":11,"highlightLines":280,"language":696,"size":546},"cl applications:login -o \u003CorganizationSlug> -i \u003CclientId> -s \u003CclientSecret> -a \u003CapplicationAlias>",{"_modelApiKey":367,"copy":730,"listStyle":369,"darkTheme":11,"buttons":731,"color":371,"indent":11},"\u003Cp>The application alias can be named anything (e.g., &quot;promotions-demo&quot;). It will be associated with the application you want to log into on your terminal (this is useful when switching between multiple applications).\u003C\u002Fp>\n\n\u003Cp>Now you can nstall the \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fgithub.com\u002Fcommercelayer\u002Fcommercelayer-cli-plugin-resources\u002F\">resources plugin\u003C\u002Fa> using the command below:\u003C\u002Fp>\n",[],{"_modelApiKey":543,"code":733,"showLineNumbers":11,"highlightLines":280,"language":696,"size":546},"cl plugins:install resources",{"_modelApiKey":367,"copy":735,"listStyle":369,"darkTheme":11,"buttons":736,"color":371,"indent":11},"\u003Cp>To create an external promotion associated with a name, promotion URL, activation rules (the start time, expire time, and currency) attributes use the command below:\u003C\u002Fp>\n",[],{"_modelApiKey":543,"code":738,"showLineNumbers":11,"highlightLines":280,"language":696,"size":546},"cl res:create external_promotions -a \\\n\tname=\"My Birthday Promotion\" \\\n\tpromotion_url=\"https:\u002F\u002F16xy12aiwl.execute-api.eu-south-1.amazonaws.com\u002Fdefault\u002FBirthdayPromoTutorial\" \\\n\tstarts_at=\"2022-06-10T12:00:21.000Z\" \\\n\texpires_at=\"2022-06-12T23:00:00.000Z\" \\\n\tcurrency_code=\"EUR\"",{"_modelApiKey":367,"copy":740,"listStyle":369,"darkTheme":11,"buttons":741,"color":371,"indent":11},"\u003Cp>Once done successfully, your external promotion should be created with the object below:\u003C\u002Fp>\n",[],{"_modelApiKey":543,"code":743,"showLineNumbers":11,"highlightLines":280,"language":545,"size":546},"{\n  id: 'ZJMmjfRgzJ',\n  type: 'external_promotions',\n  name: 'My Birthday Promotion',\n  currency_code: 'EUR',\n  starts_at: '2022-06-10T12:00:21.000Z',\n  expires_at: '2022-06-12T23:00:00.000Z',\n  total_usage_limit: null,\n  total_usage_count: 0,\n  active: false,\n  created_at: '2022-06-29T10:23:51.937Z',\n  updated_at: '2022-06-29T10:23:51.937Z',\n  reference: null,\n  reference_origin: null,\n  metadata: {},\n  promotion_url: 'https:\u002F\u002F16xy12aiwl.execute-api.eu-south-1.amazonaws.com\u002Fdefault\u002FBirthdayPromoTutorial'\n}",{"_modelApiKey":367,"copy":745,"listStyle":369,"darkTheme":11,"buttons":746,"color":371,"indent":11},"\u003Cp>Let’s give the new setup a try. To do so, we will generate a checkout link using the \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fgithub.com\u002Fcommercelayer\u002Fcommercelayer-cli-plugin-checkout\">checkout plugin\u003C\u002Fa> of the CLI and we’ll then see the behavior of the promo in our hosted checkout application. To install the plugin use the command below:\u003C\u002Fp>\n",[],{"_modelApiKey":543,"code":748,"showLineNumbers":11,"highlightLines":280,"language":696,"size":546},"cl plugins:install checkout",{"_modelApiKey":367,"copy":750,"listStyle":369,"darkTheme":11,"buttons":751,"color":371,"indent":11},"\u003Cp>Now we have to login to our organisation as a registered customer so that we have an email that we can use to fetch customer birthday. To do that we&#39;ll use the \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fgithub.com\u002Fcommercelayer\u002Fcommercelayer-cli\u002Fblob\u002Fmain\u002Fdocs\u002Fapplications.md#commercelayer-applicationslogin\">applications login command\u003C\u002Fa>:\u003C\u002Fp>\n",[752],{"darkTheme":11,"alignment":706,"buttons":753},[],{"_modelApiKey":543,"code":755,"showLineNumbers":11,"highlightLines":280,"language":696,"size":546},"commercelayer applications:login -o \u003CorganizationSlug> -i \u003CclientId> -e \u003CcustomerEmail> -p \u003CcustomerPassword> -S \u003CmarketScope> -a \u003CapplicationAlias>",{"_modelApiKey":367,"copy":757,"listStyle":369,"darkTheme":11,"buttons":758,"color":371,"indent":11},"\u003Cp>It&#39;s now time to generate a checkout link for an SKU. In our case, it will be \u003Ccode>TSHIRTMMFFFFFFE63E74MXXX\u003C\u002Fcode>.\u003C\u002Fp>\n",[],{"_modelApiKey":543,"code":760,"showLineNumbers":11,"highlightLines":280,"language":696,"size":546},"cl checkout -S TSHIRTMMFFFFFFE63E74MXXX",{"_modelApiKey":367,"copy":762,"listStyle":369,"darkTheme":11,"buttons":763,"color":371,"indent":11},"\u003Cp>At this point you can copy\u002Fpaste the generated URL in the browser, and you will see a nice checkout page thanks to Commerce Layer \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fgithub.com\u002Fcommercelayer\u002Fmfe-checkout\">hosted checkout\u003C\u002Fa> application. As we generated the checkout using an authenticated customer, \u003Cstrong>the checkout will populate customer details automatically\u003C\u002Fstrong>:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":765,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":766,"alt":767,"title":767,"width":449,"height":768,"format":419,"focalPoint":769,"customData":770},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657882615-hosted-checkout-screenshot.jpg","Hosted checkout screenshot",1374,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":772,"listStyle":369,"darkTheme":11,"buttons":773,"color":371,"indent":11},"\u003Cp>As you can see, there’s no promotion assigned to this order. That’s because we need to add our customer to the DynamoDB of our lambda. Let&#39;s do it in the DynamoDB console:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":775,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":776,"alt":777,"title":777,"width":460,"height":778,"format":290,"focalPoint":779,"customData":780},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657642711-dynamodb-add-customer.png","DynamoDB add customer",637,{"x":292,"y":292},{},{"_modelApiKey":367,"copy":782,"listStyle":369,"darkTheme":11,"buttons":783,"color":371,"indent":11},"\u003Cp>Now you can just refresh the checkout page to see a nice \u003Cstrong>€15,00\u003C\u002Fstrong> birthday discount appear:\u003C\u002Fp>\n",[],{"_modelApiKey":373,"border":7,"caption":280,"image":785,"size":381,"darkTheme":11,"rounded":7,"color":371},{"url":786,"alt":787,"title":787,"width":449,"height":768,"format":419,"focalPoint":788,"customData":789},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1657882820-checkout-discounted.jpg","Checkout discounted",{"x":292,"y":292},{},{"_modelApiKey":367,"copy":791,"listStyle":369,"darkTheme":11,"buttons":792,"color":371,"indent":11},"\u003Ch3>Conclusion\u003C\u002Fh3>\n\n\u003Cp>External promotions are a really powerful feature as they can bring promotions to a whole new level, allowing merchants and builders to have promotional logic that can based on external systems (such as CRMs) or even external promotion engines.\u003C\u002Fp>\n\n\u003Cp>We believe that \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fcommercelayer.io\u002Fdocs\u002Fcore-concepts\u002Fcomposable-commerce\">composable commerce\u003C\u002Fa> is all about \u003Cstrong>putting the power into the hands of the developer, enabling them to turn any experience they can dream up into a shoppable moment\u003C\u002Fstrong>, like a birthday promotion calculated at runtime. For example, we just illustrated how a tool like AWS can work with Commerce Layer to deliver real benefits of the composable commerce architecture.  \u003C\u002Fp>\n\n\u003Cp>If you’ve created something similar with AWS Lambda and have a question about how to work with Commerce Layer, or just want to share it with us, please \u003Ca target=\"_blank\" href=\"https:\u002F\u002Fslack.commercelayer.app\u002F\">reach out\u003C\u002Fa>! We would love to share your work with our community as well.\u003C\u002Fp>\n",[],{"displayTitle":794,"cta":303,"ctaText":280,"cards":795},"Your next read",[796,814,832,846],{"_modelApiKey":212,"link":797,"subtitle":801,"date":801,"author":802,"additionalAuthors":809,"icon":810,"abstract":811,"tag":812,"displayTitle":798,"cardRatio":300,"featured":813},{"text":798,"isExternal":11,"url":280,"link":799},"Introducing our hosted checkout application.",{"_modelApiKey":212,"slug":800},"introducing-our-hosted-checkout-application","January 20, 2022",{"name":803,"picture":804},"Simone Fiorucci",{"url":805,"alt":806,"title":806,"width":289,"height":289,"format":290,"focalPoint":807,"customData":808},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1614874508-commercelayeravatarsquareff7e27.png","Simone Fiorucci — Marketing Manager at Commerce Layer",{"x":292,"y":292},{},[],"shopping-cart","Not only an out-of-the-box, hosted payment solution for developers who are working with Commerce Layer and looking for a one-stop integration, but also and above all a powerful reference tool to build your own custom checkout experience with unlimited flexibility.",{"color":298,"name":8},0,{"_modelApiKey":212,"link":815,"subtitle":819,"date":819,"author":820,"additionalAuthors":827,"icon":828,"abstract":829,"tag":830,"displayTitle":816,"cardRatio":11,"featured":831},{"text":816,"isExternal":11,"url":280,"link":817},"A comprehensive guide to Commerce Layer webhooks.",{"_modelApiKey":212,"slug":818},"a-comprehensive-guide-to-commerce-layer-webhooks","June 9, 2022",{"name":821,"picture":822},"Bolaji Ayodeji",{"url":823,"alt":824,"title":824,"width":289,"height":289,"format":419,"focalPoint":825,"customData":826},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1643036479-bolaji.jpg","Bolaji Ayodeji — Developer Advocate at Commerce Layer",{"x":292,"y":292},{},[],"webhooks-logo","We provides a list of specific events you can monitor and transmit real-time data from using webhooks. In this article, we will introduce our webhooks mechanism and guide you through a couple of simple demo examples so that you can see it in action.",{"color":298,"name":299},1,{"_modelApiKey":212,"link":833,"subtitle":837,"date":837,"author":838,"additionalAuthors":842,"icon":843,"abstract":844,"tag":845,"displayTitle":834,"cardRatio":11,"featured":38},{"text":834,"isExternal":11,"url":280,"link":835},"How to build an international ecommerce website with Sanity and Commerce Layer.",{"_modelApiKey":212,"slug":836},"how-to-build-an-international-ecommerce-website-with-sanity-and-commerce-layer","April 6, 2022",{"name":821,"picture":839},{"url":823,"alt":824,"title":824,"width":289,"height":289,"format":419,"focalPoint":840,"customData":841},{"x":292,"y":292},{},[],"shapes","Learn how to integrate Sanity with Commerce Layer by looking at how we built our starter template using Sanity Studio, Next.js, and our React components library.",{"color":298,"name":299},{"_modelApiKey":212,"link":847,"subtitle":851,"date":851,"author":852,"additionalAuthors":856,"icon":857,"abstract":858,"tag":859,"displayTitle":848,"cardRatio":11,"featured":860},{"text":848,"isExternal":11,"url":280,"link":849},"How to make any website shoppable with Commerce Layer.",{"_modelApiKey":212,"slug":850},"how-to-make-any-website-shoppable-with-commerce-layer","February 14, 2022",{"name":821,"picture":853},{"url":823,"alt":824,"title":824,"width":289,"height":289,"format":419,"focalPoint":854,"customData":855},{"x":292,"y":292},{},[],"eyedropper","The fastest way to get started with headless commerce and Commerce Layer. Let's build a simple ecommerce product page with static content, make it shoppable using Commerce Layer's dropin javascript library, and deploy the application to the cloud.",{"color":298,"name":299},3,[862,863,865,867,869,871,873,875,877,879,881,883,885,887,889,891,893],{"attributes":303,"tag":304,"content":305},{"attributes":864,"tag":309,"content":303},{"property":308,"content":305},{"attributes":866,"tag":309,"content":303},{"name":312,"content":305},{"attributes":868,"tag":309,"content":303},{"name":315,"content":316},{"attributes":870,"tag":309,"content":303},{"property":319,"content":316},{"attributes":872,"tag":309,"content":303},{"name":322,"content":316},{"attributes":874,"tag":309,"content":303},{"property":325,"content":326},{"attributes":876,"tag":309,"content":303},{"property":329,"content":330},{"attributes":878,"tag":309,"content":303},{"property":333,"content":334},{"attributes":880,"tag":309,"content":303},{"name":337,"content":326},{"attributes":882,"tag":309,"content":303},{"property":340,"content":341},{"attributes":884,"tag":309,"content":303},{"property":344,"content":345},{"attributes":886,"tag":309,"content":303},{"property":348,"content":349},{"attributes":888,"tag":309,"content":303},{"property":352,"content":353},{"attributes":890,"tag":309,"content":303},{"property":356,"content":280},{"attributes":892,"tag":309,"content":303},{"name":359,"content":360},{"attributes":894,"tag":309,"content":303},{"name":363,"content":364},[896],{"name":285,"picture":897},{"url":287,"alt":288,"title":288,"width":289,"height":289,"format":290,"focalPoint":898,"customData":899},{"x":292,"y":292},{},1790235727738]