diff --git a/.env.example b/.env.example
index ce91c63..cae0061 100644
--- a/.env.example
+++ b/.env.example
@@ -48,6 +48,9 @@ DB_LOCATION_TABLE=location_history
 # The name of the column that contains the primary key of each location point
 DB_LOCATION__ID=id
 
+# The name of the column that contains the device ID of each location point
+DB_LOCATION__DEVICE_ID=deviceId
+
 # The name of the column that contains the timestamp of each location point
 DB_LOCATION__TIMESTAMP=timestamp
 
diff --git a/src/db/Db.ts b/src/db/Db.ts
index a683259..934ec9c 100644
--- a/src/db/Db.ts
+++ b/src/db/Db.ts
@@ -86,6 +86,7 @@ class Db {
 
     opts.locationTableColumns = [
       'id',
+      'deviceId',
       'timestamp',
       'latitude',
       'longitude',
@@ -96,7 +97,7 @@ class Db {
       'postalCode'
     ].reduce((acc: any, name: string) => {
       acc[name] = process.env[this.prefixedEnv(name)];
-      if (!acc[name]?.length && requiredColumns[name]) {
+      if (!acc[name]?.length && (requiredColumns[name] || opts.locationUrl === opts.url)) {
         // Default to the name of the required field
         acc[name] = name;
       }
diff --git a/src/db/types/GPSData.ts b/src/db/types/GPSData.ts
index 60f7f8d..0f4103e 100644
--- a/src/db/types/GPSData.ts
+++ b/src/db/types/GPSData.ts
@@ -9,6 +9,13 @@ function GPSData(locationTableColumns: Record<string, string>): Record<string, a
     autoIncrement: true
   };
 
+  const deviceIdCol: string = locationTableColumns['deviceId'];
+  if (deviceIdCol?.length) {
+    typeDef[deviceIdCol] = {
+      type: DataTypes.UUID,
+    };
+  }
+
   typeDef[locationTableColumns['latitude']] = {
     type: DataTypes.FLOAT,
     allowNull: false
diff --git a/src/models/GPSPoint.ts b/src/models/GPSPoint.ts
index 3533f39..a4f5c58 100644
--- a/src/models/GPSPoint.ts
+++ b/src/models/GPSPoint.ts
@@ -1,25 +1,27 @@
 class GPSPoint {
-    public id: number;
-    public latitude: number;
-    public longitude: number;
-    public altitude: number | null;
-    public address: string | null;
-    public locality: string | null;
-    public country: string | null;
-    public postalCode: string | null;
-    public timestamp: Date;
+  public id: number;
+  public deviceId: string;
+  public latitude: number;
+  public longitude: number;
+  public altitude: number | null;
+  public address: string | null;
+  public locality: string | null;
+  public country: string | null;
+  public postalCode: string | null;
+  public timestamp: Date;
 
-    constructor(record: any) {
-      this.id = record.id;
-      this.latitude = record.latitude;
-      this.longitude = record.longitude;
-      this.altitude = record.altitude;
-      this.address = record.address;
-      this.locality = record.locality;
-      this.country = record.country;
-      this.postalCode = record.postalCode;
-      this.timestamp = record.timestamp;
-    }
+  constructor(record: any) {
+    this.id = record.id;
+    this.deviceId = record.deviceId;
+    this.latitude = record.latitude;
+    this.longitude = record.longitude;
+    this.altitude = record.altitude;
+    this.address = record.address;
+    this.locality = record.locality;
+    this.country = record.country;
+    this.postalCode = record.postalCode;
+    this.timestamp = record.timestamp;
+  }
 }
 
 export default GPSPoint;
diff --git a/src/repos/Location.ts b/src/repos/Location.ts
index 4f3a32a..2a0c2d2 100644
--- a/src/repos/Location.ts
+++ b/src/repos/Location.ts
@@ -18,6 +18,7 @@ class Location {
 
         return new GPSPoint({
           id: data[mappings.id],
+          deviceId: data[mappings.deviceId],
           latitude: data[mappings.latitude],
           longitude: data[mappings.longitude],
           altitude: data[mappings.altitude],
@@ -32,6 +33,103 @@ class Location {
       throw new Error(`Error parsing data: ${error}`);
     }
   }
+
+  public async getByIds(ids: number[]): Promise<GPSPoint[]> {
+    let apiResponse: any[] = [];
+
+    try {
+      apiResponse = await $db.GPSData().findAll({
+        where: {
+          id: ids
+        }
+      });
+    } catch (error) {
+      throw new Error(`Error fetching data: ${error}`);
+    }
+
+    try {
+      return apiResponse.map((p) => {
+        const data = p.dataValues;
+        const mappings: any = $db.locationTableColumns;
+
+        return new GPSPoint({
+          id: data[mappings.id],
+          deviceId: data[mappings.deviceId],
+          latitude: data[mappings.latitude],
+          longitude: data[mappings.longitude],
+          altitude: data[mappings.altitude],
+          address: data[mappings.address],
+          locality: data[mappings.locality],
+          country: data[mappings.country],
+          postalCode: data[mappings.postalCode],
+          timestamp: data[mappings.timestamp],
+        });
+      });
+    } catch (error) {
+      throw new Error(`Error parsing data: ${error}`);
+    }
+  }
+
+  public async createPoints(points: GPSPoint[]): Promise<GPSPoint[]> {
+    const mappings: any = $db.locationTableColumns;
+    // Lowercase the keys of the mappings object -
+    // some databases are case-insensitive and this will help with consistency
+    const normalizedPoints = points.map((p) =>
+      Object.entries(p).reduce((acc, [key, value]) => {
+        acc[key.toLowerCase()] = value;
+        return acc;
+      } , {} as Record<string, any>)
+    );
+
+    try {
+      return (
+        await $db.GPSData().bulkCreate(
+          normalizedPoints.map((p) => {
+            return {
+              [mappings.deviceId]: p.deviceid,
+              [mappings.latitude]: p.latitude,
+              [mappings.longitude]: p.longitude,
+              [mappings.altitude]: p.altitude,
+              [mappings.address]: p.address,
+              [mappings.locality]: p.locality,
+              [mappings.country]: p.country,
+              [mappings.postalCode]: p.postalcode,
+              [mappings.timestamp]: p.timestamp
+            }
+          },
+          { returning: true }
+        ))
+      ).map((p) => {
+        const data = p.dataValues;
+        return new GPSPoint({
+          id: data[mappings.id],
+          deviceId: data[mappings.deviceId],
+          latitude: data[mappings.latitude],
+          longitude: data[mappings.longitude],
+          altitude: data[mappings.altitude],
+          address: data[mappings.address],
+          locality: data[mappings.locality],
+          country: data[mappings.country],
+          postalCode: data[mappings.postalCode],
+          timestamp: data[mappings.timestamp],
+        });
+      });
+    } catch (error) {
+      throw new Error(`Error saving data: ${error}`);
+    }
+  }
+
+  public async deletePoints(points: number[]): Promise<void> {
+    try {
+      await $db.GPSData().destroy({
+        where: {
+          id: points
+        }
+      });
+    } catch (error) {
+      throw new Error(`Error deleting data: ${error}`);
+    }
+  }
 }
 
 export default Location;
diff --git a/src/repos/UserDevices.ts b/src/repos/UserDevices.ts
index 3497950..5a22044 100644
--- a/src/repos/UserDevices.ts
+++ b/src/repos/UserDevices.ts
@@ -17,6 +17,16 @@ class UserDevices {
     return new UserDevice(dbDevice.dataValues);
   }
 
+  public async getAll(deviceIds: string[]): Promise<UserDevice[]> {
+    const dbDevices = await $db.UserDevice().findAll({
+      where: {
+        name: deviceIds,
+      }
+    });
+
+    return dbDevices.map((d) => new UserDevice(d.dataValues));
+  }
+
   public async create(name: string, args: {
     userId: number,
   }): Promise<UserDevice> {
diff --git a/src/routes/api/v1/GPSData.ts b/src/routes/api/v1/GPSData.ts
index 6a5814c..71e7354 100644
--- a/src/routes/api/v1/GPSData.ts
+++ b/src/routes/api/v1/GPSData.ts
@@ -1,7 +1,10 @@
 import { Request, Response } from 'express';
 
 import { authenticate } from '../../../auth';
+import { AuthInfo } from '../../../auth';
 import { LocationRequest } from '../../../requests';
+import { Optional } from '../../../types';
+import { RoleName } from '../../../models';
 import ApiV1Route from './Route';
 
 class GPSData extends ApiV1Route {
@@ -9,11 +12,22 @@ class GPSData extends ApiV1Route {
     super('/gpsdata');
   }
 
+  private validateOwnership = async (deviceIds: string[], auth: AuthInfo) => {
+    const user = auth.user;
+    const notOwnedDevices = (await $repos.userDevices.getAll(deviceIds))
+      .filter((d) => d.userId !== user.id);
+
+    if (notOwnedDevices.length > 0) {
+      authenticate([RoleName.Admin]);
+    }
+  };
+
   @authenticate()
   get = async (req: Request, res: Response) => {
     let query: LocationRequest
 
     try {
+      // TODO Limit to the points that the user has access to
       query = new LocationRequest(req.query);
     } catch (error) {
       const e = `Error parsing query: ${error}`;
@@ -22,14 +36,26 @@ class GPSData extends ApiV1Route {
       return;
     }
 
-    try {
-      const gpsData = await $repos.location.getHistory(query);
-      res.json(gpsData);
-    } catch (error) {
-      const e = `Error fetching data: ${error}`;
-      console.error(e);
-      res.status(500).send(e);
-    }
+    const gpsData = await $repos.location.getHistory(query);
+    res.json(gpsData);
+  }
+
+  @authenticate()
+  post = async (req: Request, res: Response, auth: Optional<AuthInfo>) => {
+    const deviceIds = req.body.map((p: any) => p.deviceId).filter((d: any) => !!d);
+    this.validateOwnership(deviceIds, auth!);
+    await $repos.location.createPoints(req.body);
+    res.status(201).send();
+  }
+
+  @authenticate()
+  delete = async (req: Request, res: Response, auth: Optional<AuthInfo>) => {
+    const pointIds = req.body as number[];
+    const points = await $repos.location.getByIds(pointIds);
+    const deviceIds = points.map((p) => p.deviceId);
+    this.validateOwnership(deviceIds, auth!);
+    await $repos.location.deletePoints(pointIds);
+    res.status(204).send();
   }
 }