diff --git a/gracedb/api.py b/gracedb/api.py
index 3f61ca4db53a5baef1db4201a1860f95562a38cd..45b534c3fb75bf53eb6abb791074ed44afdf54de 100644
--- a/gracedb/api.py
+++ b/gracedb/api.py
@@ -1,6 +1,7 @@
 
 from django.http import HttpResponse, HttpResponseNotFound
 from django.http import HttpResponseForbidden, HttpResponseServerError
+from django.http import HttpResponseBadRequest
 from django.core.urlresolvers import reverse as django_reverse
 
 from django.conf import settings
@@ -11,7 +12,7 @@ from gracedb.models import Event, Group
 
 import os
 import urllib
-
+import errno
 
 ##################################################################
 
@@ -36,6 +37,7 @@ from rest_framework.reverse import reverse
 
 from django.contrib.auth.models import User as DjangoUser
 
+MAX_FAILED_OPEN_ATTEMPTS = 5
 
 class LigoAuthentication(authentication.BaseAuthentication):
     def authenticate(self, request):
@@ -334,7 +336,6 @@ def download(request, graceid, filename=""):
 
     return response
 
-
 class Files(APIView):
     """Files Resource"""
 
@@ -401,8 +402,103 @@ class Files(APIView):
 
         return response
 
+     def put(self, request, graceid, filename=""):
+        """ File uploader.  Implements file versioning. """
+        raise NotImplementedError()
+       
+        filename = filename or ""
 
+        try:
+            event = Event.getByGraceid(graceid)
+        except Event.DoesNotExist:
+            return HttpResponseNotFound("Event not found")
+
+        # Construct the file path just as Brian does above.
+        general = False
+        if filename.startswith("general/"):
+            filename = filename[len("general/"):]
+            general = True
 
+        filepath = os.path.join(event.datadir(general), filename)
+
+        if not os.path.exists(filepath):
+            # Awesome.  The thing does not exist.  This is the first time a file
+            # by this name is being uploaded.  
+            # Write the file as "filename,0".
+            linkpath = filepath
+            filepath += ',0'
+            filename += ',0'
+            fdest = open(filepath, 'w')
+            # Check out line 392 in the client.  I think the key name for the file is 'upload'
+            f = request.FILES['upload']
+            for chunk in f.chunks(): 
+                fdest.write(chunk)
+             fdest.close()
+
+            # Make a relative symlink.
+            os.symlink(filename,linkpath)
+
+        elif os.path.islink(filepath):
+            # Great. The thing is a symlink. We can do our version-y stuff now.
+
+            # Read contents of directory.  Establish the number of existing versions.
+            # All we need is the bare filename (i.e., not the full path)
+            # Version number starts at 1 because 0 is already taken.
+            filedir = event.datadir(general)
+            lastVersion = 1
+            for dirname, dirnames, filenames in os.walk(filedir):
+                for fname in filenames:
+                    if fname.split(',')[0] == filename:
+                        lastVersion = max(lastVersion,int(fname.split(',')[1]))
+
+            # set the link path to the original file path.
+            linkpath = filepath
+            notOpenYet = True
+            failedAttempts = 0
+            while notOpenYet:
+                # find the new filename
+                newFilename = filename + ',%d' % (lastVersion+1)
+                # update the file path according to the new filename.
+                filepath = os.path.join(filedir,newFilename)
+                try:
+                    fd = os.open(filepath, os.O_WRONLY | os.O_CREAT | os.O_EXCL)
+                    fdest = os.fdopen(fd,"w")
+                    notOpenYet = False
+                except OSError as e:
+                    if e.errno==errno.EACCES:
+                        return HttpResponseForbidden("No permission to write to event directory.")
+                    else:
+                        # Note: could also check whether e.errno==errno.EEXIST
+                        ++failedAttempts
+                        if failedAttempts >= MAX_FAILED_OPEN_ATTEMPTS:
+                            return HttpResponseServerError("Cannot open file for writing: %s" % e)
+                        # Under race conditions, increment lastVersion.
+                        if e.errno==errno.EACCES:
+                            ++lastVersion 
+            
+            # Still with me? Then write the file.
+            f = request.FILES['upload']
+            for chunk in f.chunks(): 
+                fdest.write(chunk)
+            fdest.close()
+
+            # Move the symlink.  No temporal gaps, please.
+            # XXX Not sure what this call does with an *existing* symlink.
+            os.symlink(filename,linkpath)
+
+        elif os.path.isfile(filepath):
+            # The thing is a file.  We will not allow a put request to the file
+            # resource (for now, anyway).
+            response = HttpResponseForbidden("%s is a file.  Versioning is not supported with legacy data.  Please change your filename to avoid clobbering." % filename)
+        elif not filename:
+            # Not good.  There's nothing we can do without a filename.
+            response = HttpResponseBadRequest("Must have a filename for upload.")
+        elif os.path.isdir(filepath):
+            response = HttpResponseForbidden("%s is a directory" % filename)
+        else:
+            response = HttpResponseServerError("Should not happen.")
+
+        return response
 
 class FileMeta(APIView):
     """File Metadata Resource"""