diff --git a/internal/models/candidate.go b/internal/models/candidate.go
new file mode 100644
index 0000000000000000000000000000000000000000..22a6dcf1ad181f2bce0a825725df7f18a704c9c9
--- /dev/null
+++ b/internal/models/candidate.go
@@ -0,0 +1,125 @@
+package models
+
+import (
+	"encoding/json"
+	"net/http"
+	"strconv"
+	"strings"
+
+	"forge.grandlyon.com/apoyen/elections/internal/auth"
+)
+
+func (d *DataHandler) handleCandidate(w http.ResponseWriter, r *http.Request) {
+	id, _ := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/api/Candidate/"))
+	switch method := r.Method; method {
+	case "GET":
+		switch auth.GetLoggedUserTechnical(w, r).Role {
+		case "ADMIN", "CAPTURER", "VISUALIZER":
+			d.getCandidate(w, r, id)
+		default:
+			http.Error(w, ErrorRoleOfLoggedUser, http.StatusInternalServerError)
+		}
+	case "POST":
+		switch auth.GetLoggedUserTechnical(w, r).Role {
+		case "ADMIN":
+			d.postCandidate(w, r)
+		case "CAPTURER", "VISUALIZER":
+			http.Error(w, ErrorNotAuthorizeMethodOnRessource, http.StatusMethodNotAllowed)
+		default:
+			http.Error(w, ErrorRoleOfLoggedUser, http.StatusInternalServerError)
+		}
+
+	case "PUT":
+		switch auth.GetLoggedUserTechnical(w, r).Role {
+		case "ADMIN":
+			d.putCandidate(w, r, id)
+		case "CAPTURER", "VISUALIZER":
+			http.Error(w, ErrorNotAuthorizeMethodOnRessource, http.StatusMethodNotAllowed)
+		default:
+			http.Error(w, ErrorRoleOfLoggedUser, http.StatusInternalServerError)
+		}
+	case "DELETE":
+		switch auth.GetLoggedUserTechnical(w, r).Role {
+		case "ADMIN":
+			d.deleteCandidate(w, r, id)
+		case "CAPTURER", "VISUALIZER":
+			http.Error(w, ErrorNotAuthorizeMethodOnRessource, http.StatusMethodNotAllowed)
+		default:
+			http.Error(w, ErrorRoleOfLoggedUser, http.StatusInternalServerError)
+		}
+	default:
+		http.Error(w, "method not allowed", 400)
+	}
+}
+
+func (d *DataHandler) getCandidate(w http.ResponseWriter, r *http.Request, id int) {
+	if id != 0 {
+		var o Candidate
+		if err := d.db.First(&o, id).Error; err != nil {
+			http.Error(w, ErrorIDIsMissing, http.StatusNotFound)
+			return
+		}
+		json.NewEncoder(w).Encode(o)
+	} else {
+		var o []Candidate
+		d.db.Find(&o)
+		json.NewEncoder(w).Encode(o)
+	}
+}
+
+func (d *DataHandler) postCandidate(w http.ResponseWriter, r *http.Request) {
+	var o Candidate
+	err := json.NewDecoder(r.Body).Decode(&o)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	// Check that CandidateList exist
+	var candidateList CandidateList
+	if err := d.db.First(&candidateList, o.CandidateListID).Error; err != nil {
+		http.Error(w, ErrorParentNotFound, http.StatusInternalServerError)
+		return
+	}
+
+	d.db.Create(&o)
+	d.db.Last(&o)
+	json.NewEncoder(w).Encode(o)
+}
+
+func (d *DataHandler) putCandidate(w http.ResponseWriter, r *http.Request, id int) {
+	var o Candidate
+	if err := d.db.First(&o, id).Error; err != nil {
+		http.Error(w, ErrorIDIsMissing, http.StatusNotFound)
+		return
+	}
+	var candidate Candidate
+	err := json.NewDecoder(r.Body).Decode(&candidate)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	o.FullName = candidate.FullName
+	o.Rank = candidate.Rank
+	o.CommunityCounseller = candidate.CommunityCounseller
+	o.Birthdate = candidate.Birthdate
+	o.PotentialIncompatibility = candidate.PotentialIncompatibility
+	o.Refused = candidate.Refused
+	o.Removed = candidate.Removed
+	d.db.Save(&o)
+	json.NewEncoder(w).Encode(o)
+
+}
+
+func (d *DataHandler) deleteCandidate(w http.ResponseWriter, r *http.Request, id int) {
+	if id != 0 {
+		var o Candidate
+		if err := d.db.First(&o, id).Error; err != nil {
+			http.Error(w, ErrorIDIsMissing, http.StatusNotFound)
+			return
+		}
+
+		d.db.Delete(&o)
+	} else {
+		http.Error(w, ErrorIDIsMissing, http.StatusNotFound)
+	}
+}
diff --git a/internal/models/models.go b/internal/models/models.go
index 526d8c80af755583adce5bb2e881bf0a9aea0436..b5fb2d6f4403ec70c9325a0207d3eaba8f24dfdf 100644
--- a/internal/models/models.go
+++ b/internal/models/models.go
@@ -171,13 +171,13 @@ type Candidate struct {
 	FullName                 string
 	Rank                     uint
 	CommunityCounseller      bool
-	Birthdate                time.Time
+	Birthdate                string
 	PotentialIncompatibility bool
 	Refused                  bool
 	Removed                  bool
 }
 
-// Vote represent the number of voice between a CanidateList and a Desk (+blank and null)
+// Vote represent the number of voice between a CandidateList and a Desk (+blank and null)
 type Vote struct {
 	ID              uint       `gorm:"primary_key"`
 	CreatedAt       time.Time  `json:"-"`
@@ -238,6 +238,8 @@ func (d *DataHandler) ProcessAPI(w http.ResponseWriter, r *http.Request) {
 		d.handleParty(w, r)
 	case "CandidateList":
 		d.handleCandidateList(w, r)
+	case "Candidate":
+		d.handleCandidate(w, r)
 	}
 
 }
diff --git a/internal/rootmux/admin_test.go b/internal/rootmux/admin_test.go
index 29365cf0a36e66356ca2154b3d0d357ca052adce..5478c7efdc1ac7c3a5ca2e04cf44382e94c6051b 100644
--- a/internal/rootmux/admin_test.go
+++ b/internal/rootmux/admin_test.go
@@ -110,8 +110,19 @@ func AdminTests(t *testing.T) {
 		// Update a CandidateList
 		do("PUT", "/api/CandidateList/1", xsrfHeader, `{"ID":1,"Name":"MyBigList","PartyID":1,"RoundID":1,"AreaID":1}`, 200, `{"ID":1,"Name":"MyBigList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[],"Votes":[]}`)
 
+		// Create a Candidate
+		do("POST", "/api/Candidate", xsrfHeader, `{"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`, 200, `{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`)
+		// Get a Candidate
+		do("GET", "/api/Candidate/1", xsrfHeader, ``, 200, `{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`)
+		// Get Candidates
+		do("GET", "/api/Candidate/", xsrfHeader, ``, 200, `[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`)
+		// Update a Candidate
+		do("PUT", "/api/Candidate/1", xsrfHeader, `{"ID":1,"CandidateListID":1,"FullName":"CandidateTest","Rank":2,"CommunityCounseller":false,"Birthdate":"2020-02-28","PotentialIncompatibility":true,"Refused":false,"Removed":false}`, 200, `{"ID":1,"CandidateListID":1,"FullName":"CandidateTest","Rank":2,"CommunityCounseller":false,"Birthdate":"2020-02-28","PotentialIncompatibility":true,"Refused":false,"Removed":false}`)
+
 		// TODO Update a DeskRound to Validated=true can only be done when votes are captured
 
+		// Delete a Candidate
+		do("DELETE", "/api/Candidate/1", xsrfHeader, ``, 200, ``)
 		// Delete a CandidateList
 		do("DELETE", "/api/CandidateList/1", xsrfHeader, ``, 200, ``)
 		// Delete a Party
diff --git a/internal/rootmux/capturer_test.go b/internal/rootmux/capturer_test.go
index 59c0b48661b70b06ef05fd44fa32dee4097ed9eb..6f6cc1b9bd21491fc5168e0d303c252d82661e05 100644
--- a/internal/rootmux/capturer_test.go
+++ b/internal/rootmux/capturer_test.go
@@ -115,14 +115,25 @@ func CapturerTests(t *testing.T) {
 		// Create a CandidateList should fail with 405
 		do("POST", "/api/CandidateList", xsrfHeader, `{"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1}`, 405, `You're not authorize to execute this method on this ressource.`)
 		// Get a candidateList
-		do("GET", "/api/CandidateList/1", xsrfHeader, "", 200, `{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[],"Votes":[]}`)
+		do("GET", "/api/CandidateList/1", xsrfHeader, "", 200, `{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}],"Votes":[]}`)
 		// Get all the parties
-		do("GET", "/api/CandidateList/", xsrfHeader, "", 200, `[{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[],"Votes":[]}]`)
+		do("GET", "/api/CandidateList/", xsrfHeader, "", 200, `[{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}],"Votes":[]}]`)
 		// Update a CandidateList should fail with 405
 		do("PUT", "/api/CandidateList/1", xsrfHeader, `{"ID":1,"Name":"MyBigList","PartyID":1,"RoundID":1,"AreaID":1}`, 405, `You're not authorize to execute this method on this ressource.`)
 		// Delete a CandidateList should fail with 405
 		do("DELETE", "/api/CandidateList/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`)
 
+		// Create a Candidate should fail with 405
+		do("POST", "/api/Candidate", xsrfHeader, `{"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`, 405, `You're not authorize to execute this method on this ressource.`)
+		// Get a Candidate
+		do("GET", "/api/Candidate/1", xsrfHeader, "", 200, `{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`)
+		// Get all the parties
+		do("GET", "/api/Candidate/", xsrfHeader, "", 200, `[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}]`)
+		// Update a Candidate should fail with 405
+		do("PUT", "/api/Candidate/1", xsrfHeader, `{"ID":1,"CandidateListID":1,"FullName":"CandidateTest","Rank":2,"CommunityCounseller":false,"Birthdate":"2020-02-28","PotentialIncompatibility":true,"Refused":false,"Removed":false}`, 405, `You're not authorize to execute this method on this ressource.`)
+		// Delete a Candidate should fail with 405
+		do("DELETE", "/api/Candidate/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`)
+
 		// Add deskround to capturer should fail with 405
 		do("POST", "/api/CapturerDeskRound", xsrfHeader, `{"CapturerID":1,"DeskRoundID":1}`, 405, `You're not authorize to execute this method on this ressource.`)
 		// Remove DeskRound to capturer
diff --git a/internal/rootmux/rootmux_test.go b/internal/rootmux/rootmux_test.go
index 8221960964f4b4236d4f7351ec81d26454162ecf..f9ab37b26e8feee291b6d6de7d836f4234121ba3 100644
--- a/internal/rootmux/rootmux_test.go
+++ b/internal/rootmux/rootmux_test.go
@@ -222,6 +222,7 @@ func resetDataWithData(t *testing.T) {
 		do("POST", "/api/Round", xsrfHeader, `{"ElectionID":1,"Date":"2020-06-28","Round":1}`, 200, `{"ID":1,"ElectionID":1,"Parameter":{"ID":0,"CountBlankAndNull":false,"ShowOnlyCompleted":false,"ShowMap":false},"Date":"2020-06-28","Round":1,"DeskRounds":null,"CandidateLists":null}`)
 		do("POST", "/api/Party", xsrfHeader, `{"Name":"MyGreatParty","Color":"#FFFFFF"}`, 200, `{"ID":1,"Name":"MyGreatParty","Color":"#FFFFFF","CandidateLists":null}`)
 		do("POST", "/api/CandidateList", xsrfHeader, `{"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1}`, 200, `{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":null,"Votes":null}`)
+		do("POST", "/api/Candidate", xsrfHeader, `{"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`, 200, `{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`)
 	}
 	do("POST", "/Login", noH, `{"login": "admin","password": "password"}`, 200, "")
 	init()
diff --git a/internal/rootmux/visualizer_test.go b/internal/rootmux/visualizer_test.go
index 56eeee6a2ac159b233621d2aa70eb7c06302120b..6976b1401f82d16ce72183480490e83147211a11 100644
--- a/internal/rootmux/visualizer_test.go
+++ b/internal/rootmux/visualizer_test.go
@@ -102,14 +102,25 @@ func VisualizerTests(t *testing.T) {
 		// Create a CandidateList should fail with 405
 		do("POST", "/api/CandidateList", xsrfHeader, `{"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1}`, 405, `You're not authorize to execute this method on this ressource.`)
 		// Get a candidateList
-		do("GET", "/api/CandidateList/1", xsrfHeader, "", 200, `{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[],"Votes":[]}`)
+		do("GET", "/api/CandidateList/1", xsrfHeader, "", 200, `{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}],"Votes":[]}`)
 		// Get all the parties
-		do("GET", "/api/CandidateList/", xsrfHeader, "", 200, `[{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[],"Votes":[]}]`)
+		do("GET", "/api/CandidateList/", xsrfHeader, "", 200, `[{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}],"Votes":[]}]`)
 		// Update a CandidateList should fail with 405
 		do("PUT", "/api/CandidateList/1", xsrfHeader, `{"ID":1,"Name":"MyBigList","PartyID":1,"RoundID":1,"AreaID":1}`, 405, `You're not authorize to execute this method on this ressource.`)
 		// Delete a CandidateList should fail with 405
 		do("DELETE", "/api/CandidateList/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`)
 
+		// Create a Candidate should fail with 405
+		do("POST", "/api/Candidate", xsrfHeader, `{"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`, 405, `You're not authorize to execute this method on this ressource.`)
+		// Get a Candidate
+		do("GET", "/api/Candidate/1", xsrfHeader, "", 200, `{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`)
+		// Get all the parties
+		do("GET", "/api/Candidate/", xsrfHeader, "", 200, `[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}]`)
+		// Update a Candidate should fail with 405
+		do("PUT", "/api/Candidate/1", xsrfHeader, `{"ID":1,"CandidateListID":1,"FullName":"CandidateTest","Rank":2,"CommunityCounseller":false,"Birthdate":"2020-02-28","PotentialIncompatibility":true,"Refused":false,"Removed":false}`, 405, `You're not authorize to execute this method on this ressource.`)
+		// Delete a Candidate should fail with 405
+		do("DELETE", "/api/Candidate/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`)
+
 		// Add deskround to capturer should fail with 405
 		do("POST", "/api/CapturerDeskRound", xsrfHeader, `{"CapturerID":1,"DeskRoundID":1}`, 405, `You're not authorize to execute this method on this ressource.`)
 		// Remove DeskRound to capturer