Lorem ipsum dolor sit amet consectetur. Sit aliquam interdum sodales augue varius ultricies arcu condimentum netus. Id imperdiet euismod.
We built Flowcode to be a gateway between the real and virtual worlds — a way of seamlessly connecting an offline audience to online experiences. The Flowcode API doesn’t just streamline this process, it supercharges it. In a few lines of code, engineers can programmatically create thousands of QR codes, assign behaviors to them using pre-built smart rules, and organize them into campaigns. With the right support, the API enables developers to solve some of the most challenging problems facing modern businesses: generating unique identifiers, providing information to customers, and tracking user engagement across every offline channel.
In this article, we’ll go through how to generate Flowcodes in a set of clear, simple steps. To help us, we’ll use one of the most common uses for our API: tracking offline marketing channels.
Our QR codes solve this problem by connecting offline marketing materials to online stores and websites. They can be used to track offline engagement at an extremely granular level, allowing companies to prioritize the advertisements and products that customers love and to rethink those that they don’t.
The analytics for a single Flowcode. Our software is completely GDPR and CCPA compliant, so both companies and customers can be confident about using it without violating privacy requirements.
If you’re going to be generating Flowcodes for use in marketing, you probably need a dataset that looks something like this, containing ad ids and their associated campaigns. In this guide, we’ll create campaigns for each of the unique xyz_campaign_id listings, and unique Flowcodes for each ad.
The first thing that you’ll need to use the API is a client id. This is a unique string that the Flowcode team provides to all Premium customers, and you’ll need it to access any of the API endpoints. It should look something like"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", and will be composed of a combination of letters and numbers. If you can’t find it, reach out to [email protected] and we’ll help!
The code in this guide is written in Python for the sake of readability and makes use of the pandas library for dealing with data. If you’ve never used it before, you can read about it here . This guide also assumes you have some basic knowledge about how APIs work. If you need a refresher, Hubspot has a great article about them.
Right, let’s get started!
Flowcode’s API organises QR codes through campaigns, each of which is associated with a set of QR codes. Depending on your requirements, campaigns might represent anything from geographic regions, to marketing initiatives, to time periods. For our example, campaigns represent issues of a magazine that we’re generating ads for.
The first step in generating Flowcodes is creating a set of campaigns. For our ad dataset, we can see after some analysis that we have three xyz campaigns: 916, 936 and 1178.
import pandas as pd# pandas will help us process our datasetimport requests# requests is used to send and receive information from the APIad_data = pd.read_csv("./ads.csv")# importing the dataad_data.head()# checking out its structure# cutting all unnecessary columnsad_data = ad_data.loc[:, ['ad_id',"xyz_campaign_id"]] print(ad_data['xyz_campaign_id'].unique())# [916, 936, 1178]
We’ll create Flowcode campaigns for each of these, using the batch/bulk-campaign endpoint. This requires four pieces of information:
# getting all campaign names from the datasetcampaigns = ad_data['xyz_campaign_id'].unique()# setting the endpoint for our API requestcampaign_url ="https://api.flowcode.com/v2/flowcode/batch/bulk-campaign"
# creating a new campaign for each campaign in the datasetfor campaign in campaigns:
data = {"name": f"{campaign}",
"display_name": f"{campaign} display",
"client_id":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
"reserved_urls_unique": False, }
try:
response = requests.post(campaign_url, data)
response.raise_for_status()
except requests.exceptions.HTTPError as err:
if err.response.status_code == 409:
print(f"Campaign {campaign} already exists! Skipping creation")
else:
raise err
The try/except clauses here will raise an exception if the response object is invalid (a 400 error), except if we get a 409 error, which indicates we’ve already created the campaign.
There’s also an optional image_types argument that we won’t discuss in this article, which allows you to dictate what the Flowcodes associated with the campaign will look like, including their colour scheme, size, resolution and graphics. For more detailed information, check out our API documentation.
After creating campaigns, we often want to check their configuration, which we can do by calling a GET request to the same endpoint. This request requires only two pieces of information:
# sending requests to check out each of our campaignsfor campaign in campaigns: params = { 'client_id':"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", 'name': f"{campaign}" } try: response = requests.get(campaign_url, params) response.raise_for_status() print(response.text) except requests.exceptions.HTTPError as err: raise err
This will return an object containing all of the information about each specified campaign that’s similar to the one below.
Now that we’ve created campaigns for our dataset, we can generate Flowcodes that are assigned to each one. Our API provides lots of functionality for controlling the codes that you generate, including features like redirecting users based on the time of day, day of the week, and device type. Before getting into that, though, let’s look at the straightforward way to create Flowcodes.
When we create Flowcodes for each campaign, we use the batch/bulk endpoint, which (for this simple example) requires three pieces of information:
campaign_data = []
for campaign in campaigns:# get all rows where the campaign id is equal to our active campaigncampaign_data.append(ad_data.loc[ad_data["xyz_campaign_id"] == campaign])
If you need help with Dataframe subsetting in Python, here’s an excellent overview
This turns campaign_data into a list containing three Dataframes: one for each of our campaigns.
Next, let’s generate a list of url objects for each campaign to send to the endpoint. Each url object requires three pieces of information:
We can use the id of each advertisement to set the Flowcode id, which will be useful later for connecting analytics to specific ads.
flowcodes = []# initialise an empty list to store our url datafor campaign in campaign_data: campaign_list = [] for index, row in campaign.iterrows(): campaign_list.append( {"id": f"{row.ad_id}", "url_type":"URL", "url": f"http://www.flowcode.com?id={row.ad_id}" }) flowcodes.append(campaign_list)
We now have three lists of url objects, all stored inside the Flowcodes list.
You might have noticed that we’re programmatically creating URLs of the form http://www.flowcode.com?id={row.ad_id}. This is just one way of creating reactive Flowcodes, and passes an id argument to the browser which can be used to customize the webpage based on the specific Flowcode that was scanned. If the website isn’t designed to accept such an argument, it’ll helpfully just return the normal page (http://www.flowcode.com). For other use-cases like tracking offline engagement, you might just have every code pointing to the same webpage.
After running the code above, each list in Flowcodes has become a list of objects similar to the one below:
Now, we have to send three POST requests to the batch/bulk endpoint: one for each campaign.
codes_url ="https://api.flowcode.com/v2/flowcode/batch/bulk"responses = {}# create a dictionary for storing campaigns and responses# send a request for each campaign in"flowcodes"for index, value in enumerate(flowcodes): if not value:# deals with errors caused by empty campaigns pass else: codes_data = { "client_id":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "campaign_name": f"{campaigns[index]}", "urls": flowcodes[index] } try: # send the data as a json to support the formatting of"urls" flowcode_response = requests.post(codes_url, json=codes_data) flowcode_response.raise_for_status() responses[f'{campaigns[index]}'] = flowcode_response print("Flowcodes Created") except requests.exceptions.HTTPError as err: if err.response.status_code == 409: print(f"Some URLS in Campaign {campaigns[index]} already exist! Skipping creation") else: raise err
These responses include lots of information about the Flowcodes that we generated, including their id, the URL that they redirect to (redirect_value), and, most importantly, the link to the URL where we can find the generated QR code. You can read about all of these attributes here, but for our purposes, all we need is the actual message content.
# using dictionary comprehension to return readable responsesreadable_responses = {campaign: response.json() for campaign, response in responses.items()}
Each key in readable_responses is a campaign name, and each value is a list of dictionaries containing information about Flowcodes associated with that campaign.
To use these Flowcodes in our software, we need their links, which we can extract from readable_responses.
"""Creates a dicionary called generated_urls, with campaigns as keys and a list of ids and qr code urls as values."""generated_urls = {}for campaign, urls in readable_responses.items():
generated_urls[f'{campaign}'] = []
for url in urls:
generated_urls[f'{campaign}'].append(
{"id": url['id'],
"qr_code": url['images'][0]['url']
})
This will create our final object, which captures information about all of our campaigns, the advertisements associated with them, and the urls allowing us to access all of the QR codes that we just generated.
Earlier, we mentioned that you can add rules that govern the behavior of your Flowcodes. Here, we’ll show you how to do so.
Flowcodes allow you to add conditional rules to them when creating them with a smart_rules argument. With smart rules, you can configure the response URL based on device type, day of the week, and time of day. They can be injected into Flowcodes programmatically when making the API call using a smart_rules() function:
def smart_rule_generator(id): smart_rules = { "rules": [ {"url": f"https://www.flowcode.com?type=1&id={id}", "device_type":"ANDROID" }, {"url": f"https://www.flowcode.com?type=1&id={id}", "device_type":"IOS" }, {"url": f"https://www.flowcode.com?type=2&id={id}", "days": [1, 2, 3, 4, 5], "device_type":"DESKTOP" }, {"url": f"https://www.flowcode.com?type=3&id={id}", "days": [0, 6], "device_type":"DESKTOP" } ], #"time_zone":"SCANNER" means that time of day and day of the week is # that of the person scaning the QR Code "time_zone":"SCANNER" } return smart_rulescampaign_data = []for campaign in campaigns: campaign_data.append(ad_data.loc[ad_data["xyz_campaign_id"] == campaign])for campaign in campaign_data: campaign_list = [] for index, row in campaign.iterrows(): campaign_list.append( {"id": f"{row.ad_id}", "url_type":"URL", "url": f"http://www.flowcode.com?id={row.ad_id}", "smart_rules": smart_rule_generator(row.ad_id)}) flowcodes.append(campaign_list)
These smart rules add a type=1 argument to the URL if the user’s on Android or IOS, a type=2 argument if the user is on a desktop and is searching on any weekday, and a type=3 argument if the user is on a desktop and searching on the weekend.
For our ad database, you could imagine using this to customize a store page linked to the marketing materials to show different information to mobile users.
The Flowcodes that we generated come in the form of SVG files (scalable vector graphics), which are similar to JPGs and PNGs, but preserve their resolution when rescaled.
We can use Python to download all of these images and save them to a local folder.
import os# used to make new directoriesparent_dir ="<parent_dir>"# create a new directory to store imagesroot_dir = parent_dir + '/flowcode_images'if not os.path.exists(root_dir): os.mkdir(root_dir)for campaign, urls in generated_urls.items():
campaign_dir = root_dir + f'/{campaign}'
os.mkdir(campaign_dir)
for url_object in urls:
id = url_object['id']
r = requests.get(url_object['qr_code'], allow_redirects=True)
file_url ="".join([campaign_dir, f"/{id}.svg"])
with open(file_url, 'wb') as handler:
handler.write(r.content)
print(f"Flowcodes created at {root_dir}")
This code takes in a directory path parent_dir, and creates a flowcode_images folder with subfolders for each campaign. Within each campaign folder, all Flowcodes are SVGs, with a name given by their id.
If you don’t know what a directory path is or how to find them, check out this helpful guide.
And with that, we’re done! We converted a dataset of ads into Flowcodes, created campaigns to organise them, attached smart rules to them, and converted them into a flexible file format that can be inserted almost anywhere.
Once you’ve created these Flowcodes, you can easily view and export the analytics for them from our website, allowing you to make much more informed decisions about your offline marketing.
This is just one use case for our API. With very similar steps, you can create unique identifiers for your products to track inventory, or create an interface for presenting information to your customers, or link your offline marketing directly to your online store.
A full outline of our Flowcode API is available here, and documentation for the Flowpage API and Analytics API will be available soon! Please reach out to [email protected] if you need any assistance or have any questions about the API.
This code involves a few different steps, and so to simplify it, we wrote a function that takes in all of the arguments we discussed above and generates Flowcode SVGs. Feel free to adapt it when creating your own Flowcodes with the API!
def _process_url_responses(responses): print(responses) readable_responses = {campaign: response.json() for campaign, response in responses.items()} generated_urls = {} for campaign, urls in readable_responses.items(): generated_urls[f'{campaign}'] = [] for url in urls: generated_urls[f'{campaign}'].append({"id": url['id'], "qr_code": url['images'][0]['url']}) return generated_urls
def _generate_svgs(parent_dir, generated_urls):
# create a new directory to store images root_dir = parent_dir + '/flowcode_images' if not os.path.exists(root_dir): os.mkdir(root_dir) for campaign, urls in generated_urls.items(): campaign_dir = root_dir + f'/{campaign}' os.mkdir(campaign_dir) for url_object in urls: id = url_object['id'] r = requests.get(url_object['qr_code'], allow_redirects=True) file_url ="".join([campaign_dir, f"/{id}.svg"]) with open(file_url, 'wb') as handler: handler.write(r.content)