diff --git a/.gitignore b/.gitignore
index 785615ed654538948f899331d857141a721a19ef..6a859be5227218f71c3ec02b952b288511e8adbd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,8 +3,9 @@ flowspy/settings.py
 celerybeat-schedule
 *.log
 celery_var/
-urls.py
 *~
 celeryd@*
 doc_rst/
 static/rest_framework/
+
+*.swp
diff --git a/README.md b/README.md
index 3c22ecec97dc7cbfe44584eae14422e50d679025..ce134ae62bcdf33e54e92f36ec611b59b0b8892c 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
 [![Documentation Status](https://readthedocs.org/projects/flowspy/badge/?version=latest)](https://readthedocs.org/projects/flowspy/?badge=latest)
 
-#Firewall on Demand#
+# Firewall on Demand
 
-##Description##
+## Description
 
 Firewall on Demand applies via NETCONF, flow rules to a network
 device. These rules are then propagated via e-bgp to peering routers.
@@ -29,84 +29,40 @@ flowspec capable routers. Of course FoD could apply rules directly
 (via NETCONF always) to a router and then ibgp would do the rest. In
 GRNET's case the flowspec capable device is an EX4200.
 
-**Attention**: Make sure your FoD server has ssh access to your flowspec device.
+**Attention**: Make sure your FoD server has SSH access to your flowspec device.
 
-##Installation Considerations##
+## Documentation
 
-You can find the installation instructions for Debian Wheezy (64)
-with Django 1.4.x at [Flowspy documentation](http://flowspy.readthedocs.org).
-If upgrading from a previous version bear in mind the changes introduced in Django 1.4.
+You can find detailed documentation including installation / configuration
+examples at [Flowspy documentation](http://flowspy.readthedocs.org).
 
+## Installation Considerations
 
-##Rest Api##
-FoD provides a rest api. It uses token as authentication method.
+If you are upgrading from a previous version bear in mind the changes
+introduced in Django 1.4.
 
-### Generating Tokens
-A user can generate a token for his account on "my profile" page from FoD's
-UI. Then by using this token in the header of the request he can list, retrieve,
-modify and create rules.
+## Rest Api
+FoD provides a rest api. It uses token as authentication method. For usage
+instructions & examples check the documentation.
 
-### Example Usage
-Here are some examples:
+## Limitations
 
-#### GET items
-- List all the rules your user has created (admin users can see all the rules)
+A user can belong to more than one `Peer` without any limitations.
+FoD UI polls the server to dynamically update the dashboard and the
+"Live Status" about the `Route`s they are aware of. In addition, the polling
+implementation fetches information for every `Peer` the user is associated
+with. Thus, if a user belongs to many `Peer`s too many AJAX calls will be sent
+to the backend which may result in a non responsive state. It is recommended to
+keep the peers associated with any user under 5.
 
-            curl -X GET https://fod.example.com/api/routes/ -H 'Authorization: Token <Your users token>'
 
-- Retrieve a specific rule:
-
-            curl -X GET https://fod.example.com/api/routes/<rule_id>/ -H 'Authorization: Token <Your users token>'
-
-- In order to create or modify a rule you have to use POST/PUT methods.
-
-#### POST/PUT rules
-In order to update or create rules you can follow this example:
-
-##### Foreign Keys
-In order to create/modify a rule you have to connect the rule with some foreign keys:
-
-###### Ports, Fragmentypes, protocols, thenactions
-When creating a rule, one can specify:
-
-- source port
-- destination port
-- port (if source = destination)
-
-That can be done by getting the url of the desired port instance from `/api/ports/<port_id>/`
-
-Same with Fragmentypes in `/api/fragmenttypes/<fragmenttype_id>/`, protocols in `/api/matchprotocol/<protocol_id>/` and then actions in `/api/thenactions/<action_id>/`.
-
-Since we have the urls we want to connect with the rule we want to create, we can make a POST request like the following:
-
-
-      curl -X POST -H 'Authorization: Token <Your users token>' -F "name=Example" -F "comments=Description" -F "source=0.0.0.0/0" -F "sourceport=https://fod.example.com/api/ports/7/" -F "destination=203.0.113.12" https://fod.example.com/api/routes/
-
-And here is a PUT request example:
-
-      curl -X PUT -F "name=Example" -F "comments=Description" -F "source=0.0.0.0/0" -F "sourceport=https://fod.example.com/api/ports/7/" -F "destination=83.212.9.93" https://fod.example.com/api/routes/12/ -H  'Authorization: Token <Your users token>'
-
-
-##Limitations##
-
-A user can belong to more than one peer, without any limitation. This fact may
-produce some limitations though, to FoD application. FoD uses polling for updating
-dashboard and let users know about other users' actions, who belong to the same
-peer. In order to fetch updates from all user's peers, FoD makes ajax calls for
-any one of them. It is recommended not to add more than 5 peers to any user,
-because it may cause malfunction to FoD application.
-
-
-##Contact##
-
-You can find more about FoD or raise your issues at GRNET FoD
-repository: [GRNET repo](https://code.grnet.gr/fod) or [Github repo](https://github.com/grnet/flowspy).
+## Contact 
 
 You can contact us directly at dev{at}noc[dot]grnet(.)gr
 
 ## Copyright and license
 
-Copyright © 2010-2014 Greek Research and Technology Network (GRNET S.A.)
+Copyright © 2010-2017 Greek Research and Technology Network (GRNET S.A.)
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/doc/api.md b/doc/api.md
new file mode 100644
index 0000000000000000000000000000000000000000..b0e83bd86425a306c7c92adca530e39d6499beaa
--- /dev/null
+++ b/doc/api.md
@@ -0,0 +1,381 @@
+# Description
+
+Since v1.3 FoD officially has a REST API. This allows operations on:
+
+* `ThenAction`
+* `MatchPort`
+* `MatchProtocol`
+* `MatchDscp`
+* `FragmentType`
+* `Route`
+
+The API needs authentication. Out of the box the supported authentication
+type is Token Authentication.
+
+## Generating Tokens
+
+A user can generate an API token using the FoD UI. Select "My Profile" from the
+top right menu and on the "Api Token" section click "Generate One".
+
+## Accessing the API
+
+The API is available at `/api/`. One can see the available API endpoints for 
+each model by making a GET request there. An authentication token must be added
+in the request:
+
+* Using `cURL`, add the `-H "Authorization: Token <your-token>"`
+parameter
+* Using Postman, under the "Headers" add a header with name
+"Authorization" and value "Token <your-token>".
+
+# Usage Examples
+
+Some basic usage examples will be provided including available
+actions. Examples will be provided in `cURL` form.
+
+An example will be provided for `ThenAction`. This example applies to most other
+models (`MatchPort`, `FragmentType`, `MatchProtocol`, `MatchDscp`) except
+`Route` which is more complex and will be treated separately.
+
+## ThenAction
+
+### GET
+
+#### All items
+
+URL: `/api/thenactions/`
+
+Example:
+```
+curl -X GET https://fod.example.com/api/thenactions/ -H "Authorization: Token <your-token>"
+
+RESPONSE:
+[  
+   {  
+      "id" 1,
+      "action":"discard",
+      "action_value":""
+   },
+   {  
+      "id" 3,
+      "action":"rate-limit",
+      "action_value":"10000k"
+   },
+   ...
+]
+```
+
+#### A specific item
+
+One can also GET a specific `ThenAction`, by using the `id` in the GET url
+
+URL: `/api/thenactions/<thenaction-id>/`
+
+Example:
+```
+curl -X GET https://fod.example.com/api/thenactions/13/ -H "Authorization: Token <your-token>"
+
+RESPONSE:
+{  
+   "id" 13,
+   "action":"discard",
+   "action_value":""
+},
+```
+
+### POST
+
+Here both `action`, `action_value` fields are required. 
+
+URL: `/api/thenactions/`
+
+Example:
+```
+curl -X POST https://fod.example.com/api/thenactions/ -F "action=rate-limit" -F "action_value=10k" -H "Authorization: Token <your-token>"
+
+RESPONSE:
+{
+    "id": 24,
+    "action": "rate-limit",
+    "action_value": "10k" 
+}
+```
+
+### PUT
+
+Here whichever of the `action`, `action_value` fields can be supplied
+
+URL: `/api/thenactions/<thenaction-id>/`
+
+Example:
+```
+curl -X PUT https://fod.example.com/api/thenactions/24/ -F "action=rate-limit" -F "action_value=10k" -H "Authorization: Token <your-token>"
+
+RESPONSE:
+{
+    "id": 24,
+    "action": "rate-limit",
+    "action_value": "10k" 
+}
+```
+### DELETE
+
+URL: `/api/thenactions/<thenaction-id>/`
+
+Example:
+```
+curl -X DELETE https://fod.example.com/api/thenactions/24/ -H "Authorization: Token <your-token>"
+
+RESPONSE:
+NO CONTENT
+```
+
+## Route
+
+### GET
+
+#### All items
+
+URL: `/api/routes/`
+
+Example:
+```
+curl -X GET https://fod.example.com/api/routes/ -H "Authorization: Token <your-token>"
+
+RESPONSE:
+[
+  {
+    "name": "nonadmin_safts_4T0ABD",
+    "id": 1,
+    "comments": "testing rule myman",
+    "applier": "admin",
+    "source": "62.217.45.76/32",
+    "sourceport": [],
+    "destination": "62.217.45.88/32",
+    "destinationport": [],
+    "port": [],
+    "dscp": [],
+    "fragmenttype": [],
+    "icmpcode": "",
+    "packetlength": null,
+    "protocol": [],
+    "tcpflag": "",
+    "then": [],
+    "filed": "2017-03-28T14:51:33Z",
+    "last_updated": "2017-03-28T14:51:33Z",
+    "status": "INACTIVE",
+    "expires": "2017-04-04",
+    "response": "Successfully committed",
+    "requesters_address": "83.212.9.94"
+  },
+   ...
+]
+```
+
+#### A specific item
+
+One can also GET a specific `Route`, by using the `id` in the GET url
+
+URL: `/api/routes/<route-id>/`
+
+Example:
+```
+curl -X GET https://fod.example.com/api/routes/1/ -H "Authorization: Token <your-token>"
+
+RESPONSE:
+{
+    "name": "nonadmin_safts_4T0ABD",
+    "id": 1,
+    "comments": "testing rule myman",
+    "applier": "admin",
+    "source": "62.217.45.76/32",
+    "sourceport": [],
+    "destination": "62.217.45.88/32",
+    "destinationport": [],
+    "port": [],
+    "dscp": [],
+    "fragmenttype": [],
+    "icmpcode": "",
+    "packetlength": null,
+    "protocol": [],
+    "tcpflag": "",
+    "then": [],
+    "filed": "2017-03-28T14:51:33Z",
+    "last_updated": "2017-03-28T14:51:33Z",
+    "status": "INACTIVE",
+    "expires": "2017-04-04",
+    "response": "Successfully committed",
+    "requesters_address": "83.212.9.94"
+}
+```
+
+### POST
+
+Required fields:
+
+* `name`: a name for the route
+* `source`: a source subnet in CIDR formation
+* `destination`: a destination subnet in CIDR formation
+* `comments`: a small comment on what this route is about
+
+The response will contain all the additional fields
+
+URL: `/api/routes/`
+
+Example:
+```
+curl -X POST https://fod.example.com/api/routes/ -F "source=62.217.45.75/32" -F "destination=62.217.45.91/32" -F "name=testroute" -F "comments=Route for testing" -H "Authorization: Token <your-token>"
+
+RESPONSE:
+{
+    "name": "testroute_ODUI3E",
+    "id": 3,
+    "comments": "Route for testing",
+    "applier": "admin",
+    "source": "62.217.45.76/32",
+    "sourceport": [],
+    "destination": "62.217.45.90/32",
+    "destinationport": [],
+    "port": [],
+    "dscp": [],
+    "fragmenttype": [],
+    "icmpcode": null,
+    "packetlength": null,
+    "protocol": [],
+    "tcpflag": null,
+    "then": [],
+    "filed": "2017-03-29T13:56:45.860Z",
+    "last_updated": "2017-03-29T13:56:45.860Z",
+    "status": "PENDING",
+    "expires": "2017-04-05",
+    "response": null,
+    "requesters_address": null
+}
+```
+
+Notice that the `Route` has a `PENDING` status. This happens because the `Route`
+is applied asynchronously to the Flowspec device (the API does not wait for the
+operation). After a while the `Route` application will be finished and the 
+`status` field will contain the updated status (`ACTIVE`, `ERROR` etc).
+You can check this `Route`s status by issuing a `GET` request with the `id`
+the API returned.
+
+This `Route`, however, is totally useless, since it applies no action for the
+matched traffic. Let's add one with a `then` action which will discard it.
+
+To do that, we must first add a `ThenAction` (or pick one of the already
+existing) since we need it's `id`. Let's assume a `ThenAction` with an `id` of
+`4` exists. To create a new `Route` with this `ThenAction`:
+
+```
+curl -X POST https://fod.example.com/api/routes/ -F "source=62.217.45.75/32" -F "destination=62.217.45.91/32" -F "name=testroute" -F "comments=Route for testing" -F "then=https://fod.example.com/api/thenactions/4" -H "Authorization: Token <your-token>"
+
+{
+    "name":"testroute_9Q5Y90",
+    "id":5,
+    "comments":"Route for testing",
+    "applier":"admin",
+    "source":"62.217.45.75/32",
+    "sourceport":[],
+    "destination":"62.217.45.94/32",
+    "destinationport":[],
+    "port":[],
+    "dscp":[],
+    "fragmenttype":[],
+    "icmpcode":null,
+    "packetlength":null,
+    "protocol":[],
+    "tcpflag":null,
+    "then":[
+       "https://fod.example.com/api/thenactions/4/"
+    ],
+    "filed":"2017-03-29T14:21:03.261Z",
+    "last_updated":"2017-03-29T14:21:03.261Z",
+    "status":"PENDING",
+    "expires":"2017-04-05",
+    "response":null,
+    "requesters_address":null
+}
+```
+
+With the same process one can associate a `Route` with the `MatchPort`,
+`FragmentType`, `MatchProtocol` & `MatchDscp` models.
+
+NOTE:
+
+When adding multiple `ForeignKey` related fields (such as multiple
+`MatchPort` or `ThenAction` items) it is best to use a `json` file on the
+request instead of specifying each field as a form argument.
+
+Example:
+
+```
+curl -X POST https://fod.example.com/api/routes/ -d@data.json -H "Authorization: Token <your-token>"
+
+data.json:
+{
+    "name": "testroute",
+    "comments": "Route for testing",
+    "then": [
+        "https://fod.example.com/api/thenactions/4",
+        "https://fod.example.com/api/thenactions/5",
+    ],
+    "source": "62.217.45.75/32",
+    "destination": "62.217.45.91/32"
+}
+
+RESPONSE:
+{
+    "name":"testroute_9Q5Y90",
+    "id":5,
+    "comments":"Route for testing",
+    "applier":"admin",
+    "source":"62.217.45.75/32",
+    "sourceport":[],
+    "destination":"62.217.45.94/32",
+    "destinationport":[],
+    "port":[],
+    "dscp":[],
+    "fragmenttype":[],
+    "icmpcode":null,
+    "packetlength":null,
+    "protocol":[],
+    "tcpflag":null,
+    "then":[
+       "https://fod.example.com/api/thenactions/4/"
+    ],
+    "filed":"2017-03-29T14:21:03.261Z",
+    "last_updated":"2017-03-29T14:21:03.261Z",
+    "status":"PENDING",
+    "expires":"2017-04-05",
+    "response":null,
+    "requesters_address":null
+}
+```
+
+### PUT, PATCH
+
+`Route` objects can be modified using the `PUT` / `PATCH` HTTP methods.
+
+When using `PUT` all fields should be specified (see `POST` section).
+However, when using `PATCH` one can specify single fields too. This is useful
+for changing the `status` of an `INACTIVE` `Route` to `ACTIVE`.
+
+The process is the same as described above with `POST`. Don't forget to use
+the correct method.
+
+### DELETE
+
+See `ThenAction`s.
+
+### General notes on `Route` models:
+
+* When `POST`ing a new `Route`, FoD will automatically commit it to the flowspec
+device. Thus, `POST`ing a new `Route` with a status of `INACTIVE` has no effect,
+since the `Route` will be activated and the status will be restored to `ACTIVE`.
+* When `DELETE`ing a `Route`, the actual `Route` object will remain. FoD will
+only delete the rule from the flowspec device and change the `Route`'s status to
+'INACTIVE'
+* When changing (`PUT`/`PATCH`) a `Route`, FoD will sync the changes to the
+flowspec device. Changing the status of the `Route` will activate / delete the
+rule respectively.
diff --git a/doc/index.md b/doc/index.md
index e902b7bac7355522538c1bd4ee2dce54f2adffd4..27d4a5c555d936c21d3dfdb868fd640665140846 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -26,7 +26,7 @@ case the flowspec capable device is an EX4200.
 
 > ** Attention **
 >
-> Make sure your FoD server has ssh access to your flowspec device.
+> Make sure your FoD server has SSH access to your flowspec device.
 
 # Contact
 
@@ -37,14 +37,12 @@ You can contact us directly at dev{at}noc[dot]grnet(.)gr
 
 # Repositories
 
-  - [GRNET FoD repository](https://code.grnet.gr/projects/flowspy)
-
   - [Github FoD repository](https://github.com/grnet/flowspy)
 
 
 ## Copyright and license
 
-Copyright © 2010-2014 Greek Research and Technology Network (GRNET S.A.)
+Copyright © 2010-2017 Greek Research and Technology Network (GRNET S.A.)
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -58,5 +56,3 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-
diff --git a/flowspec/models.py b/flowspec/models.py
index 64319df607266e890d56b6347dbc5d27e05d216f..5e92810f0c95e6e37d2ff1cddcfbdbe67a917137 100644
--- a/flowspec/models.py
+++ b/flowspec/models.py
@@ -178,9 +178,9 @@ class Route(models.Model):
 
     def save(self, *args, **kwargs):
         if not self.pk:
-            hash = id_gen()
-            self.name = "%s_%s" % (self.name, hash)
-        super(Route, self).save(*args, **kwargs) # Call the "real" save() method.
+            suff = id_gen()
+            self.name = "%s_%s" % (self.name, suff)
+        super(Route, self).save(*args, **kwargs)
 
     def clean(self, *args, **kwargs):
         from django.core.exceptions import ValidationError
diff --git a/flowspec/serializers.py b/flowspec/serializers.py
index 287b53355dd47b8edfa158520b80e122d986a545..26b3b4f215f4aa84b941c62a6931b0eaa3ad746a 100644
--- a/flowspec/serializers.py
+++ b/flowspec/serializers.py
@@ -1,109 +1,91 @@
+"""
+Serializers for flowspec models
+"""
 from rest_framework import serializers
 from flowspec.models import (
-    Route,
-    MatchPort,
-    ThenAction,
-    FragmentType,
-    MatchProtocol
-)
+    Route, MatchPort, ThenAction, FragmentType, MatchProtocol, MatchDscp)
 from flowspec.validators import (
-    clean_source,
-    clean_destination,
-    clean_expires,
-    check_if_rule_exists
-)
+    clean_source, clean_destination, clean_expires, clean_status)
 
 
 class RouteSerializer(serializers.HyperlinkedModelSerializer):
+    """
+    A serializer for `Route` objects
+    """
     applier = serializers.CharField(source='applier_username', read_only=True)
 
-    def validate(self, data):
+    def validate_source(self, attrs, source):
         user = self.context.get('request').user
-        # validate source
-        source = data.get('source')
-        res = clean_source(
-            user,
-            source
-        )
-        if res != source:
+        source_ip = attrs.get('source')
+        res = clean_source(user, source_ip)
+        if res != source_ip:
             raise serializers.ValidationError(res)
+        return attrs
 
-        # validate destination
-        destination = data.get('destination')
-        res = clean_destination(
-            user,
-            destination
-        )
+    def validate_destination(self, attrs, source):
+        user = self.context.get('request').user
+        destination = attrs.get('destination')
+        res = clean_destination(user, destination)
         if res != destination:
             raise serializers.ValidationError(res)
+        return attrs
 
-        # validate expires
-        expires = data.get('expires')
-        res = clean_expires(
-            expires
-        )
+    def validate_expires(self, attrs, source):
+        expires = attrs.get('expires')
+        res = clean_expires(expires)
         if res != expires:
             raise serializers.ValidationError(res)
+        return attrs
 
-        # check if rule already exists with different name
-        fields = {
-            'source': data.get('source'),
-            'destination': data.get('destination'),
-        }
-        exists = check_if_rule_exists(fields)
-        if exists:
-            raise serializers.ValidationError(exists)
-        return data
+    def validate_status(self, attrs, source):
+        status = attrs.get('status')
+        res = clean_status(status)
+        if res != status:
+            raise serializers.ValidationError(res)
+        return attrs
 
     class Meta:
         model = Route
         fields = (
-            'name',
-            'id',
-            'comments',
-            'applier',
-            'source',
-            'sourceport',
-            'destination',
-            'destinationport',
-            'port',
-            'dscp',
-            'fragmenttype',
-            'icmpcode',
-            'packetlength',
-            'protocol',
-            'tcpflag',
-            'then',
-            'filed',
-            'last_updated',
-            'status',
-            'expires',
-            'response',
-            'comments',
-            'requesters_address',
-        )
-        read_only_fields = ('status', 'expires', 'requesters_address', 'response')
+            'name', 'id', 'comments', 'applier', 'source', 'sourceport',
+            'destination', 'destinationport', 'port', 'dscp', 'fragmenttype',
+            'icmpcode', 'packetlength', 'protocol', 'tcpflag', 'then', 'filed',
+            'last_updated', 'status', 'expires', 'response', 'comments',
+            'requesters_address')
+        read_only_fields = (
+            'requesters_address', 'response', 'last_updated', 'id', 'filed')
 
 
 class PortSerializer(serializers.HyperlinkedModelSerializer):
     class Meta:
         model = MatchPort
-        fields = ('port', )
+        fields = ('id', 'port', )
+        read_only_fields = ('id', )
 
 
 class ThenActionSerializer(serializers.HyperlinkedModelSerializer):
     class Meta:
         model = ThenAction
-        fields = ('action', 'action_value')
+        fields = ('id', 'action', 'action_value')
+        read_only_fields = ('id', )
 
 
 class FragmentTypeSerializer(serializers.HyperlinkedModelSerializer):
     class Meta:
         model = FragmentType
-        fields = ('fragmenttype', )
+        fields = ('id', 'fragmenttype', )
+        read_only_fields = ('id', )
 
 
 class MatchProtocolSerializer(serializers.HyperlinkedModelSerializer):
     class Meta:
         model = MatchProtocol
-        fields = ('protocol', )
+        fields = ('id', 'protocol', )
+        read_only_fields = ('id', )
+
+
+class MatchDscpSerializer(serializers.HyperlinkedModelSerializer):
+    class Meta:
+        model = MatchDscp
+        fields = ('id', 'dscp', )
+        read_only_fields = ('id', )
diff --git a/flowspec/validators.py b/flowspec/validators.py
index af39f6f71f4e6c18bff9a2038f296242b5578b2b..efa55b2ae827870a0029ebb90bdc80b9b9c83413 100644
--- a/flowspec/validators.py
+++ b/flowspec/validators.py
@@ -28,6 +28,27 @@ def clean_ip(address):
             return _('Malformed address format. Cannot be ...255/32')
 
 
+def clean_status(status):
+    """
+    Verifies the `status` of a `Route` is valid.
+    Only allows `ACTIVE` / `INACTIVE` states since the rest should be
+    assigned from the application
+
+    :param status: the status of a `Route`
+    :type status: str
+
+    :returns: Either the status or a validation error message
+    :rtype: str
+    """
+
+    allowed_states = ['ACTIVE', 'INACTIVE']
+
+    if status not in allowed_states:
+        return _('Invalid status value. You are allowed to use "{}".'.format(
+            ', '.join(allowed_states)))
+    return status
+
+
 def clean_source(user, source):
     success, address = get_network(source)
     if not success:
@@ -88,10 +109,12 @@ def clean_destination(user, destination):
 def clean_expires(date):
     if date:
         range_days = (date - datetime.date.today()).days
-        if range_days > 0 and range_days < 11:
+        if range_days > 0 and range_days < settings.MAX_RULE_EXPIRE_DAYS:
             return date
         else:
-            return _('Invalid date range')
+            return _(
+                'Invalid date range. A rule cannot remain active '
+                'for more than {} days'.format(settings.MAX_RULE_EXPIRE_DAYS))
 
 
 def value_list_to_list(valuelist):
@@ -143,11 +166,40 @@ def clean_route_form(data):
         return _('This action "%s" is not permitted') % (then[0].action)
 
 
-def check_if_rule_exists(fields):
+def check_if_rule_exists(fields, queryset):
+    """
+    Checks if a `Route` object with the same source / destination
+    addresses exists in a queryset. If not, it checks any `Route`
+    object (belonging to any user) exists with the same addresses
+    and reports respectively
+
+    :param fields: the source / destination IP addresses
+    :type fields: dict
+
+    :param queryset: the queryset with the user's `Route` objects
+    :type queryset: `django.db.models.query.QuerySet`
+
+    :returns: if the rule exists or not, a message
+    :rtype: tuple(bool, str)
+    """
+
+    routes = queryset.filter(
+        source=fields.get('source'),
+        destination=IPNetwork(fields.get('destination')).compressed,
+    )
+    if routes:
+        ids = [str(item[0]) for item in routes.values_list('pk')]
+        return (
+            True, _('Rule(s) regarding those addresses already exist '
+                    'with id(s) {}. Please edit those instead'.format(', '.join(ids))))
+
     routes = Route.objects.filter(
         source=fields.get('source'),
         destination=IPNetwork(fields.get('destination')).compressed,
     )
     for route in routes:
-        return _('Rule exists with id %s and status %s. Please edit it.' % (route.id, route.status))
-    return False
+        return (
+            True, _('Rule(s) regarding those addresses already exist '
+                    'but you cannot edit them. Please refer to the '
+                    'application\'s administrators for further clarification'))
+    return (False, None)
diff --git a/flowspec/views.py b/flowspec/views.py
index bdf652a294fd915a12b126dfda9350bd5b4a4eef..c999ed2ab14208f5731d45c7f1a1d921a8c8c38a 100644
--- a/flowspec/views.py
+++ b/flowspec/views.py
@@ -295,8 +295,7 @@ def add_route(request):
         form = RouteForm(request_data)
         if form.is_valid():
             route = form.save(commit=False)
-            if not request.user.is_superuser:
-                route.applier = request.user
+            route.applier = User.objects.get(username=request.user.username)
             route.status = "PENDING"
             route.response = "Applying"
             route.source = IPNetwork('%s/%s' % (IPNetwork(route.source).network.compressed, IPNetwork(route.source).prefixlen)).compressed
@@ -376,8 +375,7 @@ def edit_route(request, route_slug):
             route.name = route_original.name
             route.status = route_original.status
             route.response = route_original.response
-            if not request.user.is_superuser:
-                route.applier = request.user
+            route.applier = User.objects.get(username=request.user.username)
             if bool(set(changed_data) & set(critical_changed_values)) or (not route_original.status == 'ACTIVE'):
                 route.status = "PENDING"
                 route.response = "Applying"
@@ -465,8 +463,7 @@ def delete_route(request, route_slug):
         if applier_peer == requester_peer or request.user.is_superuser:
             route.status = "PENDING"
             route.expires = datetime.date.today()
-            if not request.user.is_superuser:
-                route.applier = request.user
+            route.applier = User.objects.get(username=request.user.username)
             route.response = "Deactivating"
             try:
                 route.requesters_address = request.META['HTTP_X_FORWARDED_FOR']
diff --git a/flowspec/viewsets.py b/flowspec/viewsets.py
index ee6f9ea17eaaae7e6fd23d2b9cf22145e8d0eeda..04bc7dac51104c5daf3deb0300a6f9c72585a186 100644
--- a/flowspec/viewsets.py
+++ b/flowspec/viewsets.py
@@ -4,12 +4,8 @@ from rest_framework.exceptions import PermissionDenied
 
 from rest_framework import viewsets
 from flowspec.models import (
-    Route,
-    MatchPort,
-    ThenAction,
-    FragmentType,
-    MatchProtocol
-)
+    Route, MatchPort, ThenAction, FragmentType, MatchProtocol,
+    MatchDscp)
 
 from flowspec.serializers import (
     RouteSerializer,
@@ -17,8 +13,9 @@ from flowspec.serializers import (
     ThenActionSerializer,
     FragmentTypeSerializer,
     MatchProtocolSerializer,
-)
+    MatchDscpSerializer)
 
+from flowspec.validators import check_if_rule_exists
 from rest_framework.response import Response
 
 
@@ -37,22 +34,115 @@ class RouteViewSet(viewsets.ModelViewSet):
 
         if self.request.user.is_superuser:
             return Route.objects.all()
-        elif self.request.user.is_authenticated and not self.request.user.is_anonymous:
+        elif (self.request.user.is_authenticated() and not
+              self.request.user.is_anonymous()):
             return Route.objects.filter(applier=self.request.user)
 
     def list(self, request):
-        serializer = RouteSerializer(self.get_queryset(), many=True, context={'request': request})
+        serializer = RouteSerializer(
+            self.get_queryset(), many=True, context={'request': request})
         return Response(serializer.data)
 
     def create(self, request):
-        serializer = RouteSerializer(context={'request': request})
-        return super(RouteViewSet, self).create(request)
+        serializer = RouteSerializer(
+            context={'request': request}, data=request.DATA, partial=True)
+        if serializer.is_valid():
+            (exists, message) = check_if_rule_exists(
+                {'source': serializer.object.source,
+                 'destination': serializer.object.destination},
+                self.get_queryset())
+            if exists:
+                return Response({"non_field_errors": [message]}, status=400)
+            else:
+                return super(RouteViewSet, self).create(request)
+        else:
+            return Response(serializer.errors, status=400)
 
     def retrieve(self, request, pk=None):
         route = get_object_or_404(self.get_queryset(), pk=pk)
-        serializer = RouteSerializer(route)
+        serializer = RouteSerializer(route, context={'request': request})
         return Response(serializer.data)
 
+    def update(self, request, pk=None, partial=False):
+        """
+        Overriden to customize `status` update behaviour.
+        Changes in `status` need to be handled here, since we have to know the
+        previous `status` of the object to choose the correct action.
+        """
+
+        def set_object_pending(obj):
+            """
+            Sets an object's status to "PENDING". This reflects that
+            the object has not already been commited to the flowspec device,
+            and the asynchronous job that will handle the sync will
+            update the status accordingly
+
+            :param obj: the object whose status will be changed
+            :type obj: `flowspec.models.Route`
+            """
+            obj.status = "PENDING"
+            obj.response = "N/A"
+            obj.save()
+
+        def work_on_active_object(obj, new_status):
+            """
+            Decides which `commit` action to choose depending on the
+            requested status
+
+            Cases:
+            * `ACTIVE` ~> `INACTIVE`: The `Route` must be deleted from the
+                flowspec device (`commit_delete`)
+            * `ACTIVE` ~> `ACTIVE`: The `Route` is present, so it must be
+                edited (`commit_edit`)
+
+            :param new_status: the newly requested status
+            :type new_status: str
+            :param obj: the `Route` object
+            :type obj: `flowspec.models.Route`
+            """
+            set_object_pending(obj)
+            if new_status == 'INACTIVE':
+                obj.commit_delete()
+            else:
+                obj.commit_edit()
+
+        def work_on_inactive_object(obj, new_status):
+            """
+            Decides which `commit` action to choose depending on the
+            requested status
+
+            Cases:
+            * `INACTIVE` ~> `ACTIVE`: The `Route` is not present on the device
+
+            :param new_status: the newly requested status
+            :type new_status: str
+            :param obj: the `Route` object
+            :type obj: `flowspec.models.Route`
+            """
+            if new_status == 'ACTIVE':
+                set_object_pending(obj)
+                obj.commit_add()
+
+        obj = get_object_or_404(self.queryset, pk=pk)
+        old_status = obj.status
+
+        serializer = RouteSerializer(
+            obj, context={'request': request},
+            data=request.DATA, partial=partial)
+
+        if serializer.is_valid():
+            new_status = serializer.object.status
+            super(RouteViewSet, self).update(request, pk, partial=partial)
+            if old_status == 'ACTIVE':
+                work_on_active_object(obj, new_status)
+            elif old_status in ['INACTIVE', 'ERROR']:
+                work_on_inactive_object(obj, new_status)
+            return Response(
+                RouteSerializer(obj,context={'request': request}).data,
+                status=200)
+        else:
+            return Response(serializer.errors, status=400)
+
     def pre_save(self, obj):
         # DEBUG
         if settings.DEBUG:
@@ -69,9 +159,6 @@ class RouteViewSet(viewsets.ModelViewSet):
     def post_save(self, obj, created):
         if created:
             obj.commit_add()
-        else:
-            if obj.status not in ['EXPIRED', 'INACTIVE', 'ADMININACTIVE']:
-                obj.commit_edit()
 
     def pre_delete(self, obj):
         obj.commit_delete()
@@ -95,3 +182,8 @@ class FragmentTypeViewSet(viewsets.ModelViewSet):
 class MatchProtocolViewSet(viewsets.ModelViewSet):
     queryset = MatchProtocol.objects.all()
     serializer_class = MatchProtocolSerializer
+
+
+class MatchDscpViewSet(viewsets.ModelViewSet):
+    queryset = MatchDscp.objects.all()
+    serializer_class = MatchDscpSerializer
diff --git a/flowspy/settings.py.dist b/flowspy/settings.py.dist
index 27b658207caabab7e55cefe42d3fd4ca405e803d..2fb6d8ef04adaf504354900e3b05eb946bb66f9f 100644
--- a/flowspy/settings.py.dist
+++ b/flowspy/settings.py.dist
@@ -157,6 +157,8 @@ INSTALLED_APPS = (
     'djcelery',
     'peers',
     'registration',
+    'rest_framework',
+    'rest_framework.authtoken',
     'accounts',
     'tinymce',
     'widget_tweaks',
@@ -301,6 +303,7 @@ ACCOUNT_ACTIVATION_DAYS = 7
 
 # Define subnets that should not have any rules applied whatsoever
 PROTECTED_SUBNETS = ['10.10.0.0/16']
+MAX_RULE_EXPIRE_DAYS = 10
 
 # Add two whois servers in order to be able to get all the subnets for an AS.
 PRIMARY_WHOIS = 'whois.example.com'
@@ -348,43 +351,25 @@ REST_FRAMEWORK = {
     ]
 }
 
-# Limit of ports in 'ports' / 'SrcPorts' / 'DstPorts' of a rule:
-PORTRANGE_LIMIT = 100
-
-# Statistics polled via SNMP:
-# Default community string
-SNMP_COMMUNITY = "abcd"
-
-# list of IP addresses, each IP is a dict with "ip", "port" (optional, default
-# is 161), "community" (optional, default is SNMP_COMMUNITY) keys
-SNMP_IP = [
-    {"ip": "192.168.0.1", "port": 1000},
-    {"ip": "192.168.0.2", "port": 1001, "community": "abcdef"},
-    {"ip": "192.168.0.3", "port": 1002},
-    {"ip": "192.168.0.4", "port": 1002}
-]
-
-# or simpler way of IP list:
-# SNMP_IP = ["10.0.0.1", "10.0.0.2"]
-
-# OID of bytes counter (currently unused)
-SNMP_CNTBYTES =     "1.3.6.1.4.1.2636.3.5.2.1.5"
-# OID of packet counter
-SNMP_CNTPACKETS =   "1.3.6.1.4.1.2636.3.5.2.1.4"
-
-# get only statistics of specified tables
-SNMP_RULESFILTER = ["__flowspec_default_inet__", "__flowspec_IAS_inet__"]
-# load new data into cache if it is older that a specified number of seconds
-SNMP_POLL_INTERVAL = 8 #seconds
-# cache file for data
-SNMP_TEMP_FILE = "/tmp/snmp_temp_data"
-
-# Number of historical values to store for a route.
-# Polling interval must be set for "snmp-stats-poll" celery task in CELERYBEAT_SCHEDULE.
-# By default, it is 5 min interval, so SNMP_MAX_SAMPLECOUNT=12 means we have about
-# one hour history.
-SNMP_MAX_SAMPLECOUNT = 12
-
-# Age of inactive routes that can be already removed (in seconds)
-SNMP_REMOVE_RULES_AFTER = 3600
+SENTRY = {
+    'activate': False,
+    'sentry_dsn': ''
+}
 
+# check local settings for sentry activation & dsn setup
+if SENTRY.get('activate'):
+    import raven
+    sentry_dsn = os.getenv("SENTRY_DSN") or SENTRY['sentry_dsn']
+    if not sentry_dsn:
+        raise RuntimeError("Sentry dsn not configured neither as environmental"
+                           " variable nor in the settings.py file")
+
+    RAVEN_CONFIG = {
+        'dsn': sentry_dsn,
+        'release': raven.fetch_git_sha(BASE_DIR)
+    }
+    INSTALLED_APPS += ('raven.contrib.django.raven_compat',)
+    LOGGING['handlers']['sentry'] = {
+        'class': 'raven.contrib.django.handlers.SentryHandler'
+    }
+    LOGGING['loggers']['django.request']['handlers'] = ['sentry']
diff --git a/flowspy/urls.py.dist b/flowspy/urls.py
similarity index 91%
rename from flowspy/urls.py.dist
rename to flowspy/urls.py
index 5e69d9e514baccfe2e46375a974f8ef92259ded3..2813f7d31adf81d3a92c5bbf2a713cc0db1e00b6 100644
--- a/flowspy/urls.py.dist
+++ b/flowspy/urls.py
@@ -1,14 +1,15 @@
 from django.conf.urls import patterns, include, url
 from django.views.generic.simple import direct_to_template
+from django.conf import settings
 from django.contrib import admin
 from rest_framework import routers
-from graphs import urls as graphs_urls
 from flowspec.viewsets import (
     RouteViewSet,
     PortViewSet,
     ThenActionViewSet,
     FragmentTypeViewSet,
     MatchProtocolViewSet,
+    MatchDscpViewSet,
 )
 
 admin.autodiscover()
@@ -20,6 +21,7 @@ router.register(r'ports', PortViewSet)
 router.register(r'thenactions', ThenActionViewSet)
 router.register(r'fragmentypes', FragmentTypeViewSet)
 router.register(r'matchprotocol', MatchProtocolViewSet)
+router.register(r'matchdscp', MatchDscpViewSet)
 
 
 urlpatterns = patterns(
@@ -60,3 +62,8 @@ urlpatterns = patterns(
     url(r'^details/(?P<route_slug>[\w\-]+)/$', 'flowspec.views.routedetails', name="route-details"),
     url(r'^routestats/(?P<route_slug>[\w\-]+)/$', 'flowspec.views.routestats', name="routestats"),
 )
+
+if 'graphs' in settings.INSTALLED_APPS:
+    from graphs import urls as graphs_urls
+    urlpatterns += (
+        '', url(r'^graphs/', include(graphs_urls)),)
diff --git a/mkdocs.yml b/mkdocs.yml
index 7452e0960f39532e49739bcdaa87c53918a5165d..505cc653d88c1a365cb09c9815167a3b6dbf1f00 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -10,3 +10,4 @@ pages:
         - 'Debian': 'installation/debian_wheezy.md'
         - 'Red Hat': 'installation/redhat.md'
     - 'Configuration': 'configuration.md'
+    - 'API': 'api.md'