Find My Kids is a self-hosted WhatsApp bot that watches the groups and chats you choose (for example, a kindergarten or school class group), runs face recognition on every image posted there, and forwards the images in which your child appears to a chat of your choice. It is built for parents who are tired of scrolling through hundreds of group photos to find the few that include their own kids.
Face recognition runs locally with DeepFace and scikit-learn. No cloud face-recognition service is used; WhatsApp access goes through the Green API.
- Features
- Components
- How It Works
- Prerequisites
- Setup Instructions
- Configuration Reference
- Usage
- API Reference
- Privacy and Security
- Troubleshooting
- Development
- Contributing
- License
- Monitors one or more WhatsApp groups or contacts per child, as defined in
config.yaml. - Local face recognition: DeepFace (VGG-Face model, OpenCV detector) creates face embeddings, and a per-child scikit-learn SVM classifier decides whether the child appears.
- Forwards matching images (the original WhatsApp message) to a target group or contact.
- Web UI to upload training images, browse the training gallery, and re-train the model.
- Bulk training by dropping images into per-child folders.
- Web page that lists your WhatsApp contacts and groups with their chat IDs, to help you fill in the config file.
- Adjustable match threshold (
PROBABILITY_THRESHOLD, see the known issue under Environment variables). - Docker image for
linux/amd64andlinux/arm64(the build workflow also targetsarmhf, so the platform list may change with the next publish).
This solution leverages the following technologies:
- Green API: Facilitates WhatsApp communication (via the
whatsapp-chatbot-pythonlibrary). - DeepFace: Detects faces and extracts face embeddings (VGG-Face model).
- scikit-learn: Trains one linear SVM classifier per child on those embeddings.
- FastAPI: Powers the web interface and API.
flowchart LR
WA[WhatsApp group / chat] --> GA[Green API instance]
GA -- "receiveNotification (polling)" --> BOT[Find My Kids bot]
BOT -- "chat ID listed in config.yaml?" --> DL[Download image]
DL --> DF["DeepFace embedding (VGG-Face)"]
DF --> CLF["Child's SVM classifier"]
CLF -- "probability >= threshold" --> FWD[Forward message to target chat]
UI[Trainer web UI :7020] --> TR[images/trainer/<collection>/]
TR --> TRAIN[Train classifiers] --> CLFS[(classifiers/*.joblib)]
CLFS --> CLF
- The bot polls the Green API notification queue for incoming messages. This is why the instance must not have a webhook URL configured (see below).
- When an image arrives from a chat ID listed under a child in
config.yaml, the bot downloads it toimages/downloaded/. - DeepFace extracts a face embedding from the image, and the child's classifier (
classifiers/<collection_id>_classifier.joblib) predicts whether it is that child. - If it is a match and the probability is at least
PROBABILITY_THRESHOLD, the original message is forwarded to thetargetchat. - The downloaded image is deleted after a successful check. If the check fails with an error, the image stays in
images/downloaded/.
Training reads every sub-folder of images/trainer/ (one folder per child), extracts one embedding per image, and trains a one-vs-all classifier for each folder. The images of the other children are the negative examples.
Note: Only the first face DeepFace detects in an image is used, both when training and when checking incoming images. Group photos in which the child is not the first detected face may be missed. For training, use images that show only the child's face.
Before proceeding with the setup, ensure that you have the following:
- Docker and Docker Compose installed
- A registered Green API account with an instance linked to a WhatsApp account that is a member of the groups you want to monitor
- Training images for at least two people (see Training)
No AWS account or other cloud service is required.
-
Visit https://green-api.com/en and register for a new account.
-
Complete the registration form by entering your details, and then click Register.
-
Once registered, select Create an instance.
-
Choose the Developer instance (Free Tier).
-
Copy the generated Instance ID and Token. You will need them for the integration.
-
To link your WhatsApp account, navigate to the API section on the left under Account and select QR. Open the provided QR URL in your browser, then click Scan QR code:
-
Scan the QR code to complete the linking process:
-
Once linked, the instance status will display a green light, indicating it is active:
Important: Do not configure a webhook URL for your instance. The bot reads incoming messages from the Green API notification queue, and Green API does not deliver notifications to that queue while a webhook URL is set.
On startup, the bot enables incoming and outgoing message notifications on the instance if all of them are disabled, and it clears any notifications that were already waiting in the queue.
-
Duplicate the sample environment file by running:
cp .env.example .env
-
Edit the
.envfile with your credentials:# WhatsApp API Credentials GREEN_API_INSTANCE=your_whatsapp_instance_id GREEN_API_TOKEN=your_whatsapp_api_token
-
Use the following
docker-compose.yaml(the same file is included in this repository):services: find-my-kids: container_name: find-my-kids image: techblog/find-my-kids:latest ports: - "7020:7020" environment: - GREEN_API_INSTANCE=${GREEN_API_INSTANCE} - GREEN_API_TOKEN=${GREEN_API_TOKEN} - PROBABILITY_THRESHOLD=0.5 volumes: - ./find-my-kids/images:/app/images - ./find-my-kids/config:/app/config - ./find-my-kids/classifiers:/app/classifiers restart: unless-stopped
Where:
./find-my-kids/imagesholds the training images (trainer/) and the temporarily downloaded images (downloaded/)../find-my-kids/configholdsconfig.yaml. A sample file is copied there on the first start../find-my-kids/classifiersholds the trained classifiers, so you don't have to re-train after the container is recreated.
-
Start the application:
docker compose up -d
-
The web interface listens on port
7020:- Trainer:
http://[SERVER_IP]:7020/trainer - Contacts and groups:
http://[SERVER_IP]:7020/contacts
- Trainer:
The image is published on Docker Hub as techblog/find-my-kids for linux/amd64 and linux/arm64.
Note: At the time of writing, the
latesttag on Docker Hub was built on 2025-04-14 and predates some later changes in this repository. Most notably, it hard-codes the threshold to0.5and ignoresPROBABILITY_THRESHOLD.Warning: An image built from the current source cannot detect faces. Every detection fails because of the
PROBABILITY_THRESHOLDissue described under Environment variables. Use the Docker Hub image until this is fixed.
The Dockerfile declares EXPOSE 80, but the application listens on port 7020. Publish port 7020 as in the compose file above.
| Variable | Required | Default | Description |
|---|---|---|---|
GREEN_API_INSTANCE |
Yes | - | Green API instance ID. |
GREEN_API_TOKEN |
Yes | - | Green API instance token. |
PROBABILITY_THRESHOLD |
No | 0.5 |
Minimum classifier probability for an image to count as a match. Ignored by the current Docker Hub latest image, which always uses 0.5. See the known issue below. |
Known issue (current source): When
PROBABILITY_THRESHOLDis set, the code keeps it as a string and compares it to a number (app/kidfinder.py), which raises an error. TheDockerfilealways setsENV PROBABILITY_THRESHOLD=(empty), so every detection in an image built from the current source fails. The0.5default applies only to a local run with the variable unset. After such an error, the bot keeps retrying the same message and stops processing new ones (see Troubleshooting).Because the classifier makes a yes/no decision, its highest probability is always at least
0.5. A threshold of0.5or lower has no effect; only raising it makes matching stricter.
The Dockerfile also declares AWS_REGION, AWS_KEY and AWS_SECRET. They are leftovers from an earlier AWS Rekognition version and are not used by the application.
The web server port (7020) is fixed in the code.
On the first start, a sample config.yaml is copied to the config folder. Edit it to look like this example:
kids:
Kid1:
collection_id: Kid1
chat_ids:
- 000000000000000000@g.us
target: 972000000000-1000000000@g.us| Key | Description |
|---|---|
kids |
One entry per child. The entry name (Kid1) is only a label. |
kids.<name>.collection_id |
Name of the child's classifier. It must match the child's training folder name under images/trainer/, and it is the value you select in the trainer UI. |
kids.<name>.chat_ids |
List of WhatsApp chats (groups ...@g.us or contacts ...@c.us) to monitor for this child. If the same chat ID is listed under more than one child, only the first child is checked. |
target |
The group or contact that matching images are forwarded to. |
To get the list of your groups and contacts with their IDs, open http://[SERVER_IP]:7020/contacts.
The web page contains a table with the list of contacts and groups:
⚠️ IMPORTANT⚠️ : The config file is read only at startup. After updating it, restart the container to reload the configuration.
Training always rebuilds the classifiers for all children from the images under images/trainer/. Each child needs their own folder, named after their collection_id, and you need images of at least two different people, because the images of the other people are used as negative examples.
To train the recognition model, open your browser and navigate to http://[SERVER_IP]:7020/trainer.
ℹ️ Notice ℹ️: An error may pop up because there are no images for the collection yet. Just click OK.
Next, select the collection you would like to train, select a picture, and click the Upload and Train button:
In the Gallery tab, you will see all the pictures used to train the model:
You can click the Re-Train button to re-train the model with the pictures.
The bot also supports bulk image upload for training. Add images to the trainer folder as follows:
images
└── trainer/
├── Kid1/
│ ├── image1.jpg
│ ├── image2.jpg
│ └── ...
├── Kid2/
│ ├── image1.jpg
│ └── ...
└── Kid3/
├── image1.jpg
└── ...
Training uses only .jpg, .jpeg and .png files. The gallery also shows .gif, .bmp and .webp files, but training skips them. Next, in the Gallery tab of the web UI, you will see all the pictures used to train the model:
Click the Re-Train button to re-train the model with the pictures.
Congrats, you can now use the bot.
When someone posts an image in a monitored chat and the child's classifier recognizes them, the bot forwards the original WhatsApp message to the target chat. Results are logged in the container logs (docker logs find-my-kids), for example Kid1 was detected in the image.
The web interface is a client of these endpoints. None of them require authentication. FastAPI's interactive documentation is available at /docs.
| Method | Path | Description |
|---|---|---|
GET |
/trainer |
Trainer web UI (upload, gallery, re-train). |
GET |
/contacts |
Web page listing WhatsApp contacts and groups. |
GET |
/chats |
JSON list of contacts and groups from Green API getContacts (cached for 60 seconds). |
GET |
/collections |
JSON list of the collection_id values from config.yaml, e.g. {"collections": ["Kid1"]}. |
GET |
/trainer/images/{collection} |
JSON list of the training image URLs for a collection. Returns 404 if the folder doesn't exist yet. |
GET |
/images/trainer/{collection}/{file} |
Serves a training image. |
POST |
/train |
Multipart form with collection and image. Saves the image to images/trainer/{collection}/ and re-trains all classifiers. The image is kept even if training fails. |
POST |
/retrain |
Form field collection (required, but only echoed back in the response). Re-trains all classifiers from the images on disk. |
DELETE |
/collection |
JSON body {"collection_id": "..."}. Left over from the AWS Rekognition version. It always fails with 500 because the helper it calls no longer exists; a body without collection_id returns 422. |
On failure, /train and /retrain return 500 with {"error": "..."}.
Example: upload a training image with curl:
curl -F "collection=Kid1" -F "image=@kid1.jpg" http://[SERVER_IP]:7020/train- Face data stays local. Training images of your children are stored in
images/trainer/, and the trained classifiers inclassifiers/. Face recognition runs inside the container. Images reach Green API and WhatsApp only as part of normal WhatsApp messaging. - No authentication. All web pages and API endpoints are open. Anyone who can reach port
7020can view the training images of your children, list your WhatsApp contacts and groups (/chats), and upload images.POST /trainalso builds file paths from the unsanitizedcollectionfield and upload filename, so an unauthenticated caller can write files into the container, including the config and the application code. Keep the port on your local network, or put it behind a reverse proxy with authentication. Do not expose it to the internet. - Credentials. Keep your Green API instance ID and token in
.env, and don't commit that file. Anyone with them can read and send messages as your linked WhatsApp account. - Consent. Group images include other people's children. Use the bot only for finding your own children, and respect the privacy rules of your groups.
- The bot doesn't react to images: Make sure the Green API instance has no webhook URL set, the instance is authorized (green light), and the group's chat ID is listed in
config.yaml. Restart the container after changing the config. Messages that arrived while the bot was offline are discarded on startup. - Training fails: You need images of at least two different people, and at least one face must be detected in the images. Check the container logs for
Error processing ...lines. - "No images" popup in the trainer: Normal for a collection that has no training folder yet. Upload the first image.
- An image isn't recognized: Only the first detected face in each image is checked. Adding more training images of the child can help. Lowering
PROBABILITY_THRESHOLDbelow0.5has no effect (see Environment variables). - The bot stops handling all messages and logs the same error every 5 seconds: An error while handling one message (a missing classifier file, a failed image download, or the
PROBABILITY_THRESHOLDissue) is not caught. The bot never removes that message from the Green API queue, so it retries it every 5 seconds and doesn't process anything else. Fix the cause, then restart the container; the queue is cleared on startup. - Missing classifier: A classifier file named
<collection_id>_classifier.joblibmust exist inclassifiers/. Train at least once after adding a child, and make sure the training folder name matches thecollection_id. - Text messages are treated as images: After the bot sees its first image, it doesn't reset its "is image" state. Later non-image messages from monitored chats are handled with the previous image's download URL.
- The first training or detection is slow: DeepFace downloads the VGG-Face model weights the first time it runs.
Project layout:
app/
app.py # FastAPI app, routes and WhatsApp bot entry point
kidfinder.py # Detection: DeepFace embedding + classifier prediction
trainer.py # Training: embeddings + one-vs-all SVM per child
utils.py # Message parsing, config loading, image download
confighandler.py # Config helper (currently unused)
config.yaml # Sample config, copied to config/ on first start
models/ # Pydantic request models
templates/ # Trainer and contacts web pages
Dockerfile
docker-compose.yaml
requirements.txt
Run locally (Python 3.12). The app uses paths relative to the working directory, so run it from app/:
pip install -r requirements.txt
cd app
export GREEN_API_INSTANCE=your_whatsapp_instance_id
export GREEN_API_TOKEN=your_whatsapp_api_token
python app.pyBuild the Docker image from source:
docker build -t find-my-kids .The image is published by the manually triggered Docker Build GitHub Actions workflow.
Issues and pull requests are welcome. Please describe the problem or change clearly and keep pull requests focused.
This project is licensed under the Apache License 2.0.














