10

I am having trouble working out how I can send multiple inline messages using the Mailgun api, from a Python app using the requests library. Currently I have (using jinja2 for templates and flask as the webframework, hosted on Heroku):

def EmailFunction(UserEmail):
    Sender = 'testing@test.co.uk'
    Subject = 'Hello World'
    Text = ''
    name = re.sub('@.*','',UserEmail)
    html = render_template('GenericEmail.html', name=name)
    images = []
    imageloc = os.path.join(dirname, 'static')
    images.append(open(os.path.join(imageloc,'img1.jpg')))
    images.append(open(os.path.join(imageloc,'img2.jpg')))
    send_mail(UserEmail,Sender,Subject,Text,html,images)
    return html

def send_mail(to_address, from_address, subject, plaintext, html, images):
    r = requests.\
        post("https://api.mailgun.net/v2/%s/messages" % app.config['MAILGUN_DOMAIN'],
            auth=("api", app.config['MAILGUN_KEY']),
             data={
                 "from": from_address,
                 "to": to_address,
                 "subject": subject,
                 "text": plaintext,
                 "html": html,
                 "inline": images
             }
         )
    return r

So the email sends fine, but no images are in the email at the end. When I click to download them they don't show. The images are referenced in the HTML as per the mailgun api (simplified of course!);

<img src="cid:img1.jpg"/>
<img src="cid:img2.jpg"/>
etc ...

Clearly I am doing something wrong, however I tried attaching these using the requests.files object, which didn't even send the email and gave no error so I assume that is not the right way at all.

Sadly the documentation on this is rather sparse.

Would it be better to have the HTML directly point the server side images? However this is not ideal as server side images in general will not be static (some will, some won't).

Piers Lillystone
  • 257
  • 1
  • 5
  • 10

2 Answers2

20

Sending Inline Images is documented here.

In the HTML, you'll reference the image like this:

<html>Inline image here: <img src="cid:test.jpg"></html>

Then, define a Multidict, to post the files to the API:

files=MultiDict([("inline", open("files/test.jpg"))])

Disclosure, I work for Mailgun. :)

Petrus Theron
  • 27,855
  • 36
  • 153
  • 287
Travis Swientek
  • 1,206
  • 9
  • 14
  • 2
    I'm adding the fact that people from Mailgun answer SO questions as reason #1024 why I love to use Mailgun (I've started 2 companies and worked for a third and all of them on Mailgun) – Jason Sperske Mar 08 '13 at 21:03
  • I just saw a bug that Mailgun filed about a year ago about this: https://github.com/kennethreitz/requests/issues/285 – Jason Sperske Mar 08 '13 at 22:32
  • I'm not sure which version fixed this, but it is definitely resolved in later versions, as we rely heavily on Multi Dictionary functionality throughout the API. If you are experiencing this problem, ping me directly and we'll find you a solution. – Travis Swientek Mar 09 '13 at 00:11
  • Hey, cheers for the help again Travis. Sadly only the first image in the multidict is getting passed to the email. I am using werkzeug.datastructures's MultiDict. However I see on this page http://documentation.mailgun.net/wrappers.html#python that with the latest version of requests there are issues with passing multidict objects and the work around won't work for files. Unfortunately it seems that as we are passing files an alternative is required. Have you any idea how this could be achieved? – Piers Lillystone Mar 10 '13 at 15:38
  • 3
    Piers - As it turns out... The latest version of Requests does not support Multidict. https://github.com/kennethreitz/requests/issues/1155. We'll update the documentation on the site to better explain this. For now, I would recommend doing something like this: files=[("inline[1]", open("test.jpg")), ("inline[2]", open("test2.jpg"))] – Travis Swientek Mar 11 '13 at 18:14
  • 3
    Hmm. I'm wondering why 'inline' files still show up as attachments using this method :\ – earthmeLon Aug 27 '13 at 21:32
  • 2
    What if the inline image was created using PIL (python imaging library) as opposed to being a file on the server? – Atul Bhatia Jun 11 '14 at 01:52
  • @TravisSwientek : Could you fix the broken link in your answer please? I bet there's more information than your excellent answer here. – MGamsby Aug 18 '17 at 12:34
  • How do you reference the image when you're using Django? – joshlsullivan Oct 23 '17 at 18:16
10

As of 2020, actual documentation here: https://documentation.mailgun.com/en/latest/api-sending.html#examples

My example:

response = requests.post(
    'https://api.mailgun.net/v3/' + YOUR_MAILGUN_DOMAIN_NAME + '/messages',
    auth=('api', YOUR_MAILGUN_API_KEY),
    files=[
        ('inline[0]', ('test1.png', open('path/filename1.png', mode='rb').read())),
        ('inline[1]', ('test2.png', open('path/filename2.png', mode='rb').read()))
    ],
    data={
        'from': 'YOUR_NAME <' + 'mailgun@' + YOUR_MAILGUN_DOMAIN_NAME + '>',
        'to': [adresat],
        'bcc': [bcc_adresat],
        'subject': 'email subject',
        'text': 'email simple text',
        'html': '''<html><body>
            <img src="cid:test1.png">
            <img src="cid:test2.png">
            </body></html>'''
    },
    timeout=5  # sec
)
Vladimir Pankov
  • 377
  • 3
  • 8