{"id":3343,"date":"2024-11-13T15:35:09","date_gmt":"2024-11-13T15:35:09","guid":{"rendered":"https:\/\/www.infobip.com\/developers\/?p=3343"},"modified":"2024-11-13T15:36:50","modified_gmt":"2024-11-13T15:36:50","slug":"co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3","status":"publish","type":"post","link":"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3","title":{"rendered":"Co-ordinating a gift exchange with Flask and Infobip (Part 3)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">This is the third part of a five-part guide that shows you how to build a web app to co-ordinate a gift exchange. We&#8217;ll use Flask, a Python web framework, and Infobip&#8217;s Python SDK.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this part of the guide, we add verification for users&#8217; phone numbers, using Infobip&#8217;s 2-factor authentication API.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can follow along with the code at the <a href=\"https:\/\/github.com\/infobip-community\/gift-exchange-with-flask\">project repository on GitHub<\/a>, where each step of this guide corresponds to <a href=\"https:\/\/github.com\/infobip-community\/gift-exchange-with-flask\/branches\">a branch in the repo<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting up our Infobip details<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is where we add some Infobip magic into our app. To get started with this, you\u2019ll need the following bits of information:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Your Infobip API key<\/li>\n\n\n\n<li>Your Infobip Base URL<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">You can manage and create API credentials at <a href=\"https:\/\/portal.infobip.com\/dev\/api-keys\">https:\/\/portal.infobip.com\/dev\/api-keys<\/a> after logging in to your Infobip account. Your Base URL is also visible in your portal homepage; see <a href=\"https:\/\/www.infobip.com\/docs\/essentials\/base-url\">the documentation<\/a> for more.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll export these credentials as environment variables by running the following commands in our terminal:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>export IB_API_KEY=&lt;your API key here&gt;\nexport IB_BASE_URL=&lt;your base URL here&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Next, we&#8217;ll set up our 2FA application within Infobip. This is where you configure things like the format of the verification PINs, how long they remain valid, and how many retries are allowed per number per day.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To do this, create a new script in your project folder called set_up_2fa_application.py and add the following code into it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from infobip_channels.sms.channel import SMSChannel\n\nchannel = SMSChannel.from_env()\n\nresponse = channel.create_tfa_application(\n    {\n    \"name\": \"gift exchange 2fa application\",\n    \"enabled\": \"true\",\n    \"configuration\": {\n        \"pinAttempts\": 7,\n        \"allowMultiplePinVerifications\": \"true\",\n        \"pinTimeToLive\": \"11m\",\n        \"verifyPinLimit\": \"2\/4s\",\n        \"sendPinPerApplicationLimit\": \"5000\/12h\",\n        \"sendPinPerPhoneNumberLimit\": \"2\/1d\",}\n    }\n\n)\n\nresponse = channel.get_tfa_applications()\napplication_id = response.list&#091;0].application_id\nprint(application_id)\n\nresponse = channel.create_tfa_message_template(\n    application_id,\n    {\n            \"pinType\": \"NUMERIC\",\n            \"pinPlaceholder\": \"{{pin}}\",\n            \"messageText\": \"Your pin is {{pin}}\",\n            \"pinLength\": 4,\n            \"language\": \"en\",\n            \"senderId\": \"Infobip 2FA\",\n            \"repeatDTMF\": \"1#\",\n            \"speechRate\": 1,\n    }\n)\ntemplate_id = response.list&#091;0].message_id\nprint(template_id)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">There are two calls to the Infobip API here, with the first being the creation of our 2FA application, and the second being the creation of a message template within that application. To learn more about this, and what each part of the configuration is doing, check out <a href=\"https:\/\/www.infobip.com\/docs\/api\/platform\/2fa\">the API documentation<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The other things that this script does is print out two IDs that we&#8217;ll need to use later. The first is the <strong>Application ID<\/strong>, and the second is the <strong>Message Template ID<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When you run this script, the Infobip Python SDK uses the credentials that you exported into your environment variables to create a <code>Channel<\/code> instance, with the <code>SMSChannel.from_env()<\/code> method.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Run this script from your terminal with the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>python set_up_2fa_application.py<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This will print the two IDs that you need to your terminal output, like this:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Application ID: &lt;Your Application ID&gt;\nTemplate ID: &lt;Your Template ID&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><br>We&#8217;ll export these to our environment variables as well:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>export TFA_APP_ID=&lt;the application ID output by the script&gt;\nexport TFA_TEMPLATE_ID=&lt;the template ID output by the script&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">To use these in our app, we&#8217;ll need to edit our app.py file. Add the following lines of code somewhere before your index() method:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>tfa_application_id = os.environ&#091;'TFA_APP_ID']\ntfa_template_id = os.environ&#091;'TFA_TEMPLATE_ID']<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now our app is able to use that 2FA application and and its message template.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Sending a verification PIN over text<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Next, we need to use this Infobip application to send our users a verification PIN when they add their number to our app.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll write a new function in our app.py file. Add this code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def send_verification_text(number):\n    channel = SMSChannel.from_env()\n    response = channel.send_pin_over_sms(\n        {\"ncNeeded\": \"false\"},\n        {\n            \"applicationId\": tfa_application_id,\n            \"messageId\": tfa_template_id,\n            \"from\": \"InfoSMS\",\n            \"to\": number,\n        }\n    )\n    save_pin_id_for_number(number, response.pin_id)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This function takes a phone number and sends a PIN to it. Infobip keeps track of verification PINs by giving them each an ID, which is stored on the Infobip end. When we want to verify that a PIN is valid, we&#8217;ll need that ID, which we get from the API&#8217;s <code>response<\/code>. So, we need to store that PIN ID somewhere in our app. This is what the function <code>save_pin_id_for_number <\/code>does, and here&#8217;s the code for that function:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def save_pin_id_for_number(number, pin_id):\n    conn = sqlite3.connect('database.db')\n    cur = conn.cursor()\n    cur.execute(f\"\"\"\n        UPDATE people\n        SET verification_code = '{pin_id}'\n        WHERE phone_number = {number};\"\"\"\n    )\n    conn.commit()\n    conn.close()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Here, we&#8217;re using SQLite3 again to store the verification PIN ID (not the same thing as the PIN itself, we&#8217;re not storing the PIN) alongside the data for each person. We do this by updating only the row where the phone number is the one which has just received a text.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Lastly, we need to actually call the <code>send_verification_text <\/code>function from our main <code>index <\/code>function. Update that code to look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@app.route('\/', methods=&#091;'GET', 'POST'])\ndef index():\n   if request.method == 'GET':\n       pass\n   else:\n       name = request.form.get('name')\n       number = request.form.get('number')\n       if not all((name, number)):\n           flash(\"Please enter both a name and a phone number.\")\n       else:\n           print(f\"You clicked the button with name {name} and number {number}!\")\n           add_new_person_to_db(name, number)\n           send_verification_text(number)\n           flash(f\"You added {name} with number {number}!\")\n   return render_template('app.html')<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now, when a user adds their name and number into our app, they&#8217;ll also get a text to their phone with a PIN code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Adding a page for users to verify their PIN<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Now, we need to build a way for users to verify that their PIN code is valid. To do this, we&#8217;ll add a \/verify page to our app. First, let&#8217;s add some HTML; create a <code>verify.html<\/code> file within your app&#8217;s <code>templates\/ <\/code>folder, and add this code to it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;html&gt;\n    &lt;head&gt;\n        &lt;title&gt;Gift Exchange App&lt;\/title&gt;\n        &lt;link rel=\"icon\" type=\"image\/favicon\" href=\"https:\/\/avatars.githubusercontent.com\/u\/1288491\"&gt;\n        &lt;link href=\"https:\/\/fonts.googleapis.com\/css2?family=Lora:wght@600&amp;display=swap\" rel=\"stylesheet\"&gt;\n    &lt;\/head&gt;\n    &lt;body&gt;\n        {% with msg = get_flashed_messages() %}\n            {% if msg %}\n                {% for message in msg %}\n                    &lt;strong&gt;{{ message }}&lt;\/strong&gt;\n                {% endfor %}\n            {% endif %}\n        {% endwith %}\n        &lt;form action=\"{{ url_for('verify')}}\" method=\"POST\"&gt;\n            &lt;p&gt;&lt;input type=\"text\" name=\"name\" placeholder=\"Enter your name here\"&gt;&lt;\/p&gt;\n            &lt;p&gt;&lt;input type=\"tel\" name=\"number\" placeholder=\"Enter your phone number here\"&gt;&lt;\/p&gt;\n            &lt;p&gt;&lt;input type=\"number\" name=\"pin\" placeholder=\"Enter your PIN code here\"&gt;&lt;\/p&gt;\n            &lt;p&gt;&lt;button type=\"submit\"&gt;&lt;p&gt;Verify my number!&lt;\/p&gt;&lt;\/button&gt;&lt;\/p&gt;\n        &lt;\/form&gt;\n    &lt;\/body&gt;\n &lt;\/html&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is very similar to the code for <code>index.html<\/code>, with the main differences being that the form has one more field &#8211; for the PIN code &#8211; and sends its data to the verify page rather than the main index page. That means we need to add code to our app to handle requests coming in to the <code>\/verify<\/code> endpoint.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In your app.py file, add the following code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@app.route('\/verify', methods=&#091;'GET', 'POST'])\ndef verify():\n   if request.method == 'GET':\n       pass\n   else:\n       name = request.form.get('name')\n       number = request.form.get('number')\n       pin = request.form.get('pin')\n       if not all((name, number, pin)):\n           flash(\"Please enter all required details.\")\n       else:\n           print(f\"You clicked the button with name {name}, number {number} and PIN {pin}!\")\n           verify_number(name, number, pin)\n   return render_template('verify.html')<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This code is, again, fairly similar to what our <code>index <\/code>code used to look like a few steps ago. The main difference here is that we&#8217;re taking three pieces of data &#8211; name, number and PIN &#8211; and instead of saving them to a database, we&#8217;re calling a <code>verify_number<\/code> function with them.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s what the verify_number code looks like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def verify_number(name, number, pin):\n    conn = sqlite3.connect('database.db')\n    cur = conn.cursor()\n    cur.execute(f\"\"\"SELECT verification_code from people\n                WHERE name = '{name}' AND phone_number = {number}\"\"\")\n    pin_id = cur.fetchone()&#091;0]\n    channel = SMSChannel.from_env()\n    response = channel.verify_phone_number(\n        pin_id,\n        {\"pin\": f\"{pin}\"}\n    )\n    if response.verified:\n        cur.execute(f\"\"\"\n            UPDATE people\n            SET verified = 1\n            WHERE phone_number = {number};\"\"\"\n        )\n        conn.commit()\n        flash(f\"Your number has been verified!\")\n    else:\n        flash(f\"Error: Number not verified: {response.pin_error}\")\n    conn.close()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This looks like a lot! Firstly, the function takes the name and number of the person who submitted the PIN, and uses that to retrieve their data from our database. Specifically, we&#8217;re retrieving the verification ID that we got when the text was originally sent. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We then use the Pin ID to query the Infobip API, and see if the PIN that the user submitted matches what Infobip has on file for that verification ID.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, we check the response from Infobip to see whether the PIN was correct or not, with <code>response.verified<\/code>. If the PIN was correct, we update the database to reflect that that user&#8217;s number is verified; if not, we send an error message to the user.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And that&#8217;s it! We&#8217;ve built a verification flow for our users, and we&#8217;re ready for them to start buying gifts for each other. In the next step, we&#8217;ll build the part of the app that tells our users who they should buy a gift for by sending them texts.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is the third part of a five-part guide [&hellip;]<\/p>\n","protected":false},"author":53,"featured_media":3111,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","footnotes":""},"categories":[28,12,255,13],"tags":[43,300,76],"coauthors":[285,299],"class_list":["post-3343","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","category-guide","category-infobip-products","category-python","tag-api","tag-python","tag-sdk"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v25.6 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Co-ordinating a gift exchange with Flask and Infobip (Part 3) - Infobip Developers Hub<\/title>\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.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Co-ordinating a gift exchange with Flask and Infobip (Part 3) - Infobip Developers Hub\" \/>\n<meta property=\"og:description\" content=\"This is the third part of a five-part guide [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3\" \/>\n<meta property=\"og:site_name\" content=\"Infobip Developers Hub\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/infobip\/\" \/>\n<meta property=\"article:published_time\" content=\"2024-11-13T15:35:09+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-11-13T15:36:50+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2024\/02\/flask2.png\" \/>\n\t<meta property=\"og:image:width\" content=\"694\" \/>\n\t<meta property=\"og:image:height\" content=\"451\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Eli Holderness, Marion Genotte\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@InfobipDev\" \/>\n<meta name=\"twitter:site\" content=\"@InfobipDev\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Eli Holderness, Marion Genotte\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3\"},\"author\":{\"name\":\"Eli Holderness\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/2d3f818bd258646bc997a3ec04146bbd\"},\"headline\":\"Co-ordinating a gift exchange with Flask and Infobip (Part 3)\",\"datePublished\":\"2024-11-13T15:35:09+00:00\",\"dateModified\":\"2024-11-13T15:36:50+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3\"},\"wordCount\":979,\"publisher\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2024\/02\/flask2.png\",\"keywords\":[\"API\",\"python\",\"SDK\"],\"articleSection\":[\"Blog Post\",\"Guide\",\"Infobip Products\",\"Python\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3\",\"url\":\"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3\",\"name\":\"Co-ordinating a gift exchange with Flask and Infobip (Part 3) - Infobip Developers Hub\",\"isPartOf\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2024\/02\/flask2.png\",\"datePublished\":\"2024-11-13T15:35:09+00:00\",\"dateModified\":\"2024-11-13T15:36:50+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#primaryimage\",\"url\":\"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2024\/02\/flask2.png\",\"contentUrl\":\"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2024\/02\/flask2.png\",\"width\":694,\"height\":451,\"caption\":\"A blog cover image showing the Python logo on the left and the Infobip logo on the right.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.infobip.com\/developers\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Co-ordinating a gift exchange with Flask and Infobip (Part 3)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#website\",\"url\":\"https:\/\/www.infobip.com\/developers\/\",\"name\":\"Infobip Developers Hub\",\"description\":\"Build meaningful customer relationships across any channel\",\"publisher\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.infobip.com\/developers\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#organization\",\"name\":\"Infobip Developers Hub\",\"url\":\"https:\/\/www.infobip.com\/developers\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2023\/03\/Infobip_logo_favicon.png\",\"contentUrl\":\"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2023\/03\/Infobip_logo_favicon.png\",\"width\":696,\"height\":696,\"caption\":\"Infobip Developers Hub\"},\"image\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/infobip\/\",\"https:\/\/x.com\/InfobipDev\",\"https:\/\/www.youtube.com\/channel\/UCUPSTy53VecI5GIir3J3ZbQ\",\"https:\/\/github.com\/infobip-community\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/2d3f818bd258646bc997a3ec04146bbd\",\"name\":\"Eli Holderness\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/image\/e3fd4a8aa6b78952d057f724bcf1dff0\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/2261319993db9030e1328c712ee20e4aaedf2c9cb8e4379ae8af57f2c877d5f4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/2261319993db9030e1328c712ee20e4aaedf2c9cb8e4379ae8af57f2c877d5f4?s=96&d=mm&r=g\",\"caption\":\"Eli Holderness\"},\"description\":\"Developer advocate, conference speaker &amp; professional nerd. Likes maths, knitting, and cats.\",\"url\":\"https:\/\/www.infobip.com\/developers\/blog\/author\/eli\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Co-ordinating a gift exchange with Flask and Infobip (Part 3) - Infobip Developers Hub","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.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3","og_locale":"en_US","og_type":"article","og_title":"Co-ordinating a gift exchange with Flask and Infobip (Part 3) - Infobip Developers Hub","og_description":"This is the third part of a five-part guide [&hellip;]","og_url":"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3","og_site_name":"Infobip Developers Hub","article_publisher":"https:\/\/www.facebook.com\/infobip\/","article_published_time":"2024-11-13T15:35:09+00:00","article_modified_time":"2024-11-13T15:36:50+00:00","og_image":[{"width":694,"height":451,"url":"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2024\/02\/flask2.png","type":"image\/png"}],"author":"Eli Holderness, Marion Genotte","twitter_card":"summary_large_image","twitter_creator":"@InfobipDev","twitter_site":"@InfobipDev","twitter_misc":{"Written by":"Eli Holderness, Marion Genotte","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#article","isPartOf":{"@id":"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3"},"author":{"name":"Eli Holderness","@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/2d3f818bd258646bc997a3ec04146bbd"},"headline":"Co-ordinating a gift exchange with Flask and Infobip (Part 3)","datePublished":"2024-11-13T15:35:09+00:00","dateModified":"2024-11-13T15:36:50+00:00","mainEntityOfPage":{"@id":"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3"},"wordCount":979,"publisher":{"@id":"https:\/\/www.infobip.com\/developers\/#organization"},"image":{"@id":"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#primaryimage"},"thumbnailUrl":"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2024\/02\/flask2.png","keywords":["API","python","SDK"],"articleSection":["Blog Post","Guide","Infobip Products","Python"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3","url":"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3","name":"Co-ordinating a gift exchange with Flask and Infobip (Part 3) - Infobip Developers Hub","isPartOf":{"@id":"https:\/\/www.infobip.com\/developers\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#primaryimage"},"image":{"@id":"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#primaryimage"},"thumbnailUrl":"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2024\/02\/flask2.png","datePublished":"2024-11-13T15:35:09+00:00","dateModified":"2024-11-13T15:36:50+00:00","breadcrumb":{"@id":"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#primaryimage","url":"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2024\/02\/flask2.png","contentUrl":"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2024\/02\/flask2.png","width":694,"height":451,"caption":"A blog cover image showing the Python logo on the left and the Infobip logo on the right."},{"@type":"BreadcrumbList","@id":"https:\/\/www.infobip.com\/developers\/blog\/co-ordinating-a-gift-exchange-with-flask-and-infobip-part-3#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.infobip.com\/developers\/"},{"@type":"ListItem","position":2,"name":"Co-ordinating a gift exchange with Flask and Infobip (Part 3)"}]},{"@type":"WebSite","@id":"https:\/\/www.infobip.com\/developers\/#website","url":"https:\/\/www.infobip.com\/developers\/","name":"Infobip Developers Hub","description":"Build meaningful customer relationships across any channel","publisher":{"@id":"https:\/\/www.infobip.com\/developers\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.infobip.com\/developers\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.infobip.com\/developers\/#organization","name":"Infobip Developers Hub","url":"https:\/\/www.infobip.com\/developers\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/logo\/image\/","url":"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2023\/03\/Infobip_logo_favicon.png","contentUrl":"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2023\/03\/Infobip_logo_favicon.png","width":696,"height":696,"caption":"Infobip Developers Hub"},"image":{"@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/infobip\/","https:\/\/x.com\/InfobipDev","https:\/\/www.youtube.com\/channel\/UCUPSTy53VecI5GIir3J3ZbQ","https:\/\/github.com\/infobip-community"]},{"@type":"Person","@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/2d3f818bd258646bc997a3ec04146bbd","name":"Eli Holderness","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/image\/e3fd4a8aa6b78952d057f724bcf1dff0","url":"https:\/\/secure.gravatar.com\/avatar\/2261319993db9030e1328c712ee20e4aaedf2c9cb8e4379ae8af57f2c877d5f4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/2261319993db9030e1328c712ee20e4aaedf2c9cb8e4379ae8af57f2c877d5f4?s=96&d=mm&r=g","caption":"Eli Holderness"},"description":"Developer advocate, conference speaker &amp; professional nerd. Likes maths, knitting, and cats.","url":"https:\/\/www.infobip.com\/developers\/blog\/author\/eli"}]}},"_links":{"self":[{"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/posts\/3343","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/users\/53"}],"replies":[{"embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/comments?post=3343"}],"version-history":[{"count":6,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/posts\/3343\/revisions"}],"predecessor-version":[{"id":3439,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/posts\/3343\/revisions\/3439"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/media\/3111"}],"wp:attachment":[{"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/media?parent=3343"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/categories?post=3343"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/tags?post=3343"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/coauthors?post=3343"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}