diff --git a/pass/app/Crypto.js b/pass/app/Crypto.js
index bff2b96e45871ba05d7bea622f006822549575c9..66161156303d072c1ab204751b13369eb7fd8956 100644
--- a/pass/app/Crypto.js
+++ b/pass/app/Crypto.js
@@ -78,13 +78,13 @@ class Crypto {
   async sign(blinded_token, private_key) {
     let min_ms = 150;
     let start = dayjs();
-    let signature = BlindSignature.sign({
+    let blinded_signature = BlindSignature.sign({
       blinded: new BigInteger(blinded_token),
       key: private_key,
     });
     let missing_ms = Math.max(min_ms - dayjs().diff(start, "millisecond"), 0);
     await new Promise((resolve) => setTimeout(resolve, missing_ms));
-    return signature;
+    return blinded_signature;
   }
 
   /**
diff --git a/pass/routes/api.js b/pass/routes/api.js
index 469599bfaf488654a6ce42b528558504f2b64efe..3f70c67611498e71241f15b42e0eaad550ce51e9 100644
--- a/pass/routes/api.js
+++ b/pass/routes/api.js
@@ -10,6 +10,7 @@ const Manual = require("../app/payment_processor/Manual");
 const Crypto = require("../app/Crypto");
 const dayjs = require("dayjs");
 const NodeRSA = require("node-rsa");
+const RedisClient = require("../app/RedisClient");
 
 router.use("/key", authorizedOnly);
 
@@ -274,7 +275,7 @@ router.post(
     .bail()
     .custom((value) => validateTokenStructure(value))
     .bail()
-    .custom(async (value) => validateTokenSignature(value)),
+    .custom(async (value) => validateTokenSignature(value, false)),
   async (req, res) => {
     const errors = validationResult(req);
     if (!errors.isEmpty()) {
@@ -299,13 +300,14 @@ router.post(
     .bail()
     .custom((value) => validateTokenStructure(value))
     .bail()
-    .custom(async (value) => validateTokenSignature(value)),
+    .custom(async (value) => validateTokenSignature(value, true)),
   async (req, res) => {
     const errors = validationResult(req);
     if (!errors.isEmpty()) {
       res.status(422).json(errors);
       return;
     }
+
     res.status(201).json({
       status: "OK",
     });
@@ -381,7 +383,12 @@ async function validateTokenStructure(value) {
       .date(1)
       .subtract(1, "month");
     let max = min.add(2, "month");
-    if (!date.isValid() || date.isBefore(min) || !date.isBefore(max)) {
+    if (
+      !token.date.match(/^\d{4}-(0[1-9]|1[1-2])$/) ||
+      !date.isValid() ||
+      date.isBefore(min) ||
+      !date.isBefore(max)
+    ) {
       error = true;
       value[i]["status"] = "format_invalid_date";
       continue;
@@ -407,12 +414,25 @@ async function validateTokenStructure(value) {
   return true;
 }
 
-async function validateTokenSignature(value) {
+async function validateTokenSignature(value, mark_used = false) {
   // Now that we checked Format of tokens: Check the signature
   let crypto = new Crypto();
   let error = false;
+
+  let redis_client = RedisClient.CLIENT();
+  let used_tokens = {};
+
   for (let i = 0; i < value.length; i++) {
     let token = value[i];
+
+    // Check if token was already used
+    let redis_hash_key = "tokens:used:" + token.date;
+    if (await redis_client.hexists(redis_hash_key, token.token)) {
+      error = true;
+      value[i]["status"] = "token_already_used";
+      continue;
+    }
+
     let verification_result = await crypto.validateToken(
       token.token,
       token.signature,
@@ -423,10 +443,49 @@ async function validateTokenSignature(value) {
       error = true;
     } else {
       value[i]["status"] = "ok";
+      // Tokens can be used. Store the used tokens
+      if (mark_used) {
+        let redis_hash_expiration = dayjs()
+          .add(2, "month")
+          .date(1)
+          .hour(0)
+          .minute(0)
+          .second(0)
+          .millisecond(0);
+        let usage_success = await redis_client
+          .pipeline()
+          .hsetnx(
+            redis_hash_key,
+            token.token,
+            dayjs().format("YYYY-MM-DD HH:mm:ss")
+          )
+          .expireat(redis_hash_key, redis_hash_expiration.unix())
+          .exec();
+        if (usage_success === 0) {
+          error = true;
+          value[i]["status"] = "token_already_used";
+        } else {
+          // Store used tokens in memory so we can rollback in case of later error
+          if (!(redis_hash_key in used_tokens)) {
+            used_tokens[redis_hash_key] = [];
+          }
+          used_tokens[redis_hash_key].push(token.token);
+        }
+      }
     }
   }
+
   if (error) {
+    if (mark_used) {
+      // Rollback any used tokens
+      for (let redis_hash_key in used_tokens) {
+        await redis_client.hdel(redis_hash_key, used_tokens[redis_hash_key]);
+      }
+    }
+    await redis_client.quit();
     return Promise.reject("Invalid Signatures");
+  } else {
+    await redis_client.quit();
   }
   return true;
 }