A simple and open-source playtesting backend for indie game developers.
Playtesters.API is a lightweight, secure RESTful service built with .NET 8 and Entity Framework Core (SQLite).
It’s designed for indie developers or small teams who need a simple way to manage playtesters, access keys, and access validation history for private or early-access game builds.
This API was originally created to support the roguelike action game I’m building with my best friend from school — but it has grown into a fully reusable, standalone solution.
- Create and manage tester accounts with unique access keys (GUIDs).
- Track successful access validations with timestamp and IP address.
- Filter and inspect tester access history by country for better monitoring and detection of shared access keys.
- Built-in IP geolocation system powered by ip-api.com, with caching to minimize API calls and improve performance.
- Secure admin endpoints using an API key stored in
.env. - Public endpoint for game clients to validate access keys.
- Public endpoint to report and accumulate playtime, allowing game clients to increment hours played.
- Easy to integrate with Unity or any custom launcher/client.
- Organized structure using use cases, services, DTOs, validators, and minimal APIs.
- Send Discord notifications via
DISCORD_WEBHOOK_URLwhenever a tester successfully validates access, allowing real-time monitoring of usage.
- .NET 8 (Web API)
- Entity Framework Core (SQLite)
- Swashbuckle (Swagger)
- FluentValidation
- SimpleResults
- DotEnv.Core
- NUnit
- FluentAssertions V7
- Install .NET 8 SDK.
- Navigate to the project directory:
cd src- Create a
.envfile:
# ---------------------------------------------------------
# Admin authentication key for protected endpoints
# ---------------------------------------------------------
API_KEY=your-admin-key
# ---------------------------------------------------------
# SQLite database file used to store testers and access logs
# ---------------------------------------------------------
SQLITE_DATA_SOURCE=playtesters.db
# ---------------------------------------------------------
# Discord webhook URL for sending notifications
# (set this only if you want Discord alerts)
# ---------------------------------------------------------
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/xxxxx/xxxxx- Run the API:
dotnet run- Access the application with this URL:
http://localhost:5183/swagger
- Build the Docker image from the root of the repo:
docker build -t playtesters-api .- Run the container without persistence (only for testing):
docker run -p 5183:8080 --env-file .env playtesters-apiIf you want the playtesters.db file to persist across restarts:
- Run the container with a mounted volume:
docker run \
-p 5183:8080 \
--env-file .env \
-v playtesters_data:/app/data \
playtesters-api- Make sure your connection string points to:
SQLITE_DATA_SOURCE=/app/data/playtesters.dbWhen the application starts, EF Core automatically applies the migrations and creates the SQLite database (along with its schema) inside the path /app/data.
This is important because the database file is generated at runtime, meaning the container writes it into the mounted volume.
By doing this, the volume does not overwrite the database path with an empty directory — instead, it simply persists the file that the app creates.
All admin endpoints require the following header:
X-Api-Key: <your-admin-key>The admin key must be defined in your .env file:
API_KEY=your-admin-keyOnly the endpoint /api/testers/validate-access is publicly accessible for validating tester access, and /api/testers/{accessKey}/playtime is publicly accessible for reporting accumulated playtime.
You can use this Playtesters.API.http file (VS Code / Rider / Visual Studio compatible) to test every endpoint of the API.
-
The
/api/testers/validate-accessendpoint should be called before allowing gameplay or enabling private build features to ensure the tester has valid access. -
The
/api/testers/{accessKey}/playtimeendpoint can be called anytime during or after gameplay to report accumulated playtime for the tester.
Below is a quick demonstration of how you can integrate the Playtesters API into a Unity login screen using a simple access key workflow.
We provide two ready-to-use scripts in assets/unity/:
-
TesterLoginMenu.cs – Handles tester login and access key validation.
- Sends the access key entered by the tester to the
/api/testers/validate-accessendpoint. - Handles success and error responses from the API, including invalid keys and server errors.
- Implements a cooldown mechanism after a configurable number of failed attempts.
- On successful login, triggers the
PlaytimeReporterto start reporting playtime and can load the main menu or other gameplay scenes. - Fully integrates with Unity UI using
TMP_InputFieldsandTMP_Textfor user feedback.
- Sends the access key entered by the tester to the
-
PlaytimeReporter.cs – Reports playtime increments to the backend.
- Sends the number of hours played since the last report to
/api/testers/{accessKey}/playtime. - The script does not send the total playtime since login, because the backend already keeps a record of total accumulated playtime. Sending the total from the start of the session would double-count the time.
- Each report calculates the time difference from the last report (
_lastReportTime) and updates_lastReportTimeafter sending. - By default, reports are sent every 2 minutes (
_reportIntervalSeconds), but this can be configured via the Inspector. - Can be attached to a persistent GameObject (
DontDestroyOnLoad) so it continues reporting across scenes.
- Sends the number of hours played since the last report to
You can find them here:
MIT License — feel free to use, modify, and extend it for your own projects.
