diff --git a/README.md b/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c415c295634db4b88fa7891a07ce3e6722217f55
--- /dev/null
+++ b/README.md
@@ -0,0 +1,40 @@
+## Start 
+
+Start the server:
+      
+      # cd src/myaktion && go run main.go
+
+## Use the REST API
+
+### Add a campaign
+   
+      # curl -H "Content-Type: application/json" -d '{"name":"Covid","organizerName":"Martin","donationMinimum":2,"targetAmount":100,"account":{"name":"Martin","bankName":"DKB","number":"123456"}}' localhost:8000/campaign
+   
+### Get campaign data
+
+To retrieve all the persisted campaign objects, send this GET request:
+
+      # curl localhost:8000/campaigns
+
+For retrieving just a specific campaign, append the campaign's ID. E.g. for retrieving the
+campaign with ID 1, call:
+
+      # curl localhost:8000/campaigns/1
+
+### Delete a campaign 
+   
+The following command deletes the campaign with ID 1:
+
+      # curl -X DELETE localhost:8000/campaigns/1
+   
+### Update a campaign 
+
+The following command updates the campaign with ID 1: 
+      
+      # curl -X PUT -H "Content-Type: application/json"  -d '{"name":"Corona","organizerName":"Marcus","donationMinimum":2,"targetAmount":100}' localhost:8000/campaigns/1
+
+### Add a donation to a campaign 
+
+This command adds a donation to the campaign with ID 1:
+
+      # curl -H "Content-Type: application/json" -d '{"Amount":20,"donorName":"Martin","receiptRequested":true,"status":"IN_PROCESS","account":{"name":"Martin","bankName":"DKB","number":"123456"}}' localhost:8000/campaigns/1/donation
diff --git a/src/myaktion/handler/campaign.go b/src/myaktion/handler/campaign.go
index 9627a21ae8d165af7c2d84114c6a51d308c02214..705a2b93e33d74933e4f9063c483aafc0e33b682 100644
--- a/src/myaktion/handler/campaign.go
+++ b/src/myaktion/handler/campaign.go
@@ -11,7 +11,7 @@ import (
 )
 
 func CreateCampaign(w http.ResponseWriter, r *http.Request) {
-	campaign, err := GetCampaign(r)
+	campaign, err := getCampaign(r)
 	if err != nil {
 		http.Error(w, err.Error(), http.StatusBadRequest)
 		return
@@ -34,7 +34,69 @@ func GetCampaigns(w http.ResponseWriter, _ *http.Request) {
 	sendJson(w, campaigns)
 }
 
-func GetCampaign(r *http.Request) (*model.Campaign, error) {
+func GetCampaign(w http.ResponseWriter, r *http.Request) {
+	id, err := getId(r)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	campaign, err := service.GetCampaign(id)
+	if err != nil {
+		log.Errorf("Failure retrieving campaign with ID %v: %v", id, err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	if campaign == nil {
+		http.Error(w, "404 campaign not found", http.StatusNotFound)
+		return
+	}
+	sendJson(w, campaign)
+}
+
+func UpdateCampaign(w http.ResponseWriter, r *http.Request) {
+	id, err := getId(r)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	campaign, err := getCampaign(r)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	campaign, err = service.UpdateCampaign(id, campaign)
+	if err != nil {
+		log.Errorf("Failure updating campaign with ID %v: %v", id, err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	if campaign == nil {
+		http.Error(w, "404 campaign not found", http.StatusNotFound)
+		return
+	}
+	sendJson(w, campaign)
+}
+
+func DeleteCampaign(w http.ResponseWriter, r *http.Request) {
+	id, err := getId(r)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	campaign, err := service.DeleteCampaign(id)
+	if err != nil {
+		log.Errorf("Failure deleting campaign with ID %v: %v", id, err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	if campaign == nil {
+		http.Error(w, "404 campaign not found", http.StatusNotFound)
+		return
+	}
+	sendJson(w, result{Success: "OK"})
+}
+
+func getCampaign(r *http.Request) (*model.Campaign, error) {
 	var campaign model.Campaign
 	err := json.NewDecoder(r.Body).Decode(&campaign)
 	if err != nil {
diff --git a/src/myaktion/handler/donation.go b/src/myaktion/handler/donation.go
new file mode 100644
index 0000000000000000000000000000000000000000..081cd4a1af72b54fe41c4be4554637164a8b6ec8
--- /dev/null
+++ b/src/myaktion/handler/donation.go
@@ -0,0 +1,42 @@
+package handler
+
+import (
+	"encoding/json"
+	"net/http"
+
+	log "github.com/sirupsen/logrus"
+
+	"gitlab.reutlingen-university.de/go-exercises/myaktion-go/src/myaktion/model"
+	"gitlab.reutlingen-university.de/go-exercises/myaktion-go/src/myaktion/service"
+)
+
+func AddDonation(w http.ResponseWriter, r *http.Request) {
+	id, err := getId(r)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	donation, err := getDonation(r)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	// TODO: if the campaign doesn't exist, return 404 - don't show FK error
+	err = service.AddDonation(id, donation)
+	if err != nil {
+		log.Errorf("Failure adding donation to campaign with ID %v: %v", id, err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	sendJson(w, donation)
+}
+
+func getDonation(r *http.Request) (*model.Donation, error) {
+	var donation model.Donation
+	err := json.NewDecoder(r.Body).Decode(&donation)
+	if err != nil {
+		log.Errorf("Can't serialize request body to donation struct: %v", err)
+		return nil, err
+	}
+	return &donation, nil
+}
diff --git a/src/myaktion/main.go b/src/myaktion/main.go
index 6b34f1d7feb72d1ea1e0c593cd7db55d5482ce65..9d36f99e604dfa5ec2901b88ff20393e46726054 100644
--- a/src/myaktion/main.go
+++ b/src/myaktion/main.go
@@ -31,6 +31,10 @@ func main() {
 	router.HandleFunc("/health", handler.Health).Methods("GET")
 	router.HandleFunc("/campaign", handler.CreateCampaign).Methods("POST")
 	router.HandleFunc("/campaigns", handler.GetCampaigns).Methods("GET")
+	router.HandleFunc("/campaigns/{id}", handler.GetCampaign).Methods("GET")
+	router.HandleFunc("/campaigns/{id}", handler.UpdateCampaign).Methods("PUT")
+	router.HandleFunc("/campaigns/{id}", handler.DeleteCampaign).Methods("DELETE")
+	router.HandleFunc("/campaigns/{id}/donation", handler.AddDonation).Methods("POST")
 	if err := http.ListenAndServe(":8000", router); err != nil {
 		log.Fatal(err)
 	}
diff --git a/src/myaktion/service/campaign.go b/src/myaktion/service/campaign.go
index 3c508034dfff10b770bf6d0ec9696d07b08abe29..33632a8c58f6e3468742c33c58443ee37926fb1c 100644
--- a/src/myaktion/service/campaign.go
+++ b/src/myaktion/service/campaign.go
@@ -1,7 +1,9 @@
 package service
 
 import (
-	"log"
+	"fmt"
+
+	log "github.com/sirupsen/logrus"
 
 	"gitlab.reutlingen-university.de/go-exercises/myaktion-go/src/myaktion/model"
 )
@@ -31,3 +33,39 @@ func GetCampaigns() ([]model.Campaign, error) {
 	}
 	return campaigns, nil
 }
+
+func GetCampaign(id uint) (*model.Campaign, error) {
+	campaign := campaignStore[id]
+	if campaign == nil {
+		return nil, fmt.Errorf("no campaign with ID %d", id)
+	}
+	log.Tracef("Retrieved: %v", campaign)
+	return campaign, nil
+}
+
+func UpdateCampaign(id uint, campaign *model.Campaign) (*model.Campaign, error) {
+	existingCampaign, err := GetCampaign(id)
+	if err != nil {
+		return existingCampaign, err
+	}
+	existingCampaign.Name = campaign.Name
+	existingCampaign.OrganizerName = campaign.OrganizerName
+	existingCampaign.TargetAmount = campaign.TargetAmount
+	existingCampaign.DonationMinimum = campaign.DonationMinimum
+	entry := log.WithField("ID", id)
+	entry.Info("Successfully updated campaign.")
+	entry.Tracef("Updated: %v", campaign)
+	return existingCampaign, nil
+}
+
+func DeleteCampaign(id uint) (*model.Campaign, error) {
+	campaign, err := GetCampaign(id)
+	if err != nil {
+		return campaign, err
+	}
+	delete(campaignStore, id)
+	entry := log.WithField("ID", id)
+	entry.Info("Successfully deleted campaign.")
+	entry.Tracef("Deleted: %v", campaign)
+	return campaign, nil
+}
diff --git a/src/myaktion/service/donation.go b/src/myaktion/service/donation.go
new file mode 100644
index 0000000000000000000000000000000000000000..ccd22a6b8b7ae2488f2602e2f05a70d0bd23afda
--- /dev/null
+++ b/src/myaktion/service/donation.go
@@ -0,0 +1,19 @@
+package service
+
+import (
+	log "github.com/sirupsen/logrus"
+
+	"gitlab.reutlingen-university.de/go-exercises/myaktion-go/src/myaktion/model"
+)
+
+func AddDonation(campaignId uint, donation *model.Donation) error {
+	campaign, err := GetCampaign(campaignId)
+	if err != nil {
+		return err
+	}
+	campaign.Donations = append(campaign.Donations, *donation)
+	entry := log.WithField("ID", campaignId)
+	entry.Info("Successfully added new donation to campaign.")
+	entry.Tracef("Stored: %v", donation)
+	return nil
+}