signal-to-inbox

June 9, 2026 ยท View on GitHub

Save signal messages as files (for your note-taking system)

Capture ideas on the go and process them on your computer when you're ready. All without leaving your favorite messenger. Ideal for your file-based note-taking system.

Create a dedicated Signal chat and have all messages send to it be created as text files on your computer. Images, PDF, audio and other attachments are also supported.

Demo

Usage

You will have a dedicated chat where you send your notes, images, etc. Once everything is setup and configured, you can process all waiting Signal messages into files with a single command:

signal-to-inbox

This also works as a cronjob. With fcron:

* */2 * * * ~/code/signal-to-inbox/signal-to-inbox
If this does not find your config

Try by pointing to your $HOME:

* */2 * * * HOME=/home/jneidel ~/code/signal-to-inbox/signal-to-inbox

Message format

Each separate message will be written to it's own file. Any sent text is accepted. If you want to explicitly set a file name for a text message you can use this pattern:

title:
body...

The title will be the file name. If the same title is used repeatedly, their contents will be appended to the same file in order. Any message passed along with an image or a PDF will become their file name. Edits are not supported and will be discarded.

Install

For now, download the repo:

git clone https://github.com/jneidel/signal-to-inbox.git

Will add a more convenient install option later.

Setup Signal

Signal provides no official API. To interact with it programmatically there is signal-cli and as a REST API interface around it signal-cli-rest-api. This setup uses the REST API.

Requirements

You will need:

  1. An unregistered Signal phone number (can be a landline, needs to be able to receive texts or calls.)
  2. A running installation of the REST API

Run the REST API

Use the official docker container to run the API (or use the equivalent Unraid app):

docker run -d \
  --name signal-api \
  -p 8080:8080 \
  -v /mnt/signal-api:/home/.local/share/signal-cli \
  -e MODE=native \
  -e SIGNAL_CLI_UID=99 \
  --restart=always \
  bbernhard/signal-cli-rest-api

Register the signal number

(See the API reference of the Signal REST API.)

Now to register a phone number for the Signal REST API.

Here is the validated step by step registration flow for the terminal:

  1. Setup env vars to avoid copy pasting:
export API_HOST=
export SIGNAL_NUMBER=

API_HOST is http://ip:port and SIGNAL_NUMBER the number you are registering starting with + and country code (e.g. +49 for DE.)

  1. Now fill out the captcha and copy the url of the "Open Signal" button (more details).
  2. Request a registration and pass the generated CAPTCHA in the body:
curl -Ss -X POST "$API_HOST/v1/register/$SIGNAL_NUMBER" -H "Content-Type: application/json" -d '{"use_voice": false, "captcha" :"CAPTCHA" }'
  1. If the number can receive SMS you can skip to 7., as you will now get the token per SMS.
  2. Otherwise you will get a 400 error. (This is expected!) Wait >=60 seconds, generate another CAPTCHA and re-register with voice confirmation:
curl -Ss -X POST "$API_HOST/v1/register/$SIGNAL_NUMBER" -H "Content-Type: application/json" -d '{"use_voice": true, "captcha" :"CAPTCHA" }'
  1. You will get a call. Be sure to write down the token you're told.
  2. Verify with your TOKEN:
curl -Ss -X POST "$API_HOST/v1/register/$SIGNAL_NUMBER/verify/TOKEN"
  1. Test that registration was successful by sending a message to YOUR_PERSONAL_NUMBER. After you accept the message request you can now also message back.
curl -Ss -X POST "$API_HOST/v2/send" -H "Content-Type: application/json" -d "{\"number\": \"$SIGNAL_NUMBER\", \"message\": \"Hi from the API\", \"recipients\": [\"YOUR_PERSONAL_NUMBER\"]}"
  1. Optional: give the account a Signal name, description and profile picture.
curl -Ss -X PUT "$API_HOST/v1/profiles/$SIGNAL_NUMBER" -H "Content-Type: application/json" -d "{ \"name\": \"My Bot\", \"about\": \"Beep boop ๐Ÿค–. I'm automated.\", \"base64_avatar\": \"$(cat inbox.png | base64 -w0 -)\" }"

Attribution for the inbox.png icon included with this repo: Flaticon

Configuration

The scripts reads a JSON config file at:

$XDG_CONFIG_HOME/signal-to-inbox/config.json

This is what an example ~/.config/signal-to-inbox/config.json could look like:

{
  "signalNumber": "+4917222222222",
  "inboxDir": "/home/jneidel/org/inbox",
  "apiHost": "http://192.168.178.23:8080",
  "messageFileExtension": "org",
  "senderAndGroupWhitelist": [
    "+49172171717",
    "nDfe1bpw9GDAm68w/i3VENs6JWqoTtBYm42DY5o3ShY="
  ],
  "orgModeMetadataScript": "generate-orgmode-metadata",
  "debugging": false,
  "backupMessages": true,
  "keepAllAttachments": false
}

Configuration values

signalNumber
The number of your bot to receive messages for (needs to be setup in signal-cli already.)
Example: "+4917222222222"

inboxDir
The directory where messages are written as files.
Example: "/home/you/org/inbox"

apiHost
host + port where signal-cli-rest-api is running.
Example: "http://192.168.178.23:8080"

messageFileExtension
The file extension used for text notes. Org mode get special formatting.
Examples: "md", "org", "txt"
Default: "md"

senderAndGroupWhitelist (optional)
If an array is supplied, only messages from senders in the list will be parsed. Everything else is ignored. Valid sender values are phone numbers and signal ids (individual or group.)
null/[]=no filtering, array with data=whitelist active
Values: null/[]/array of strings
Example: ["+49171717171", "nDfe1bpw9GDAm68w/i3VENs6JWqoTtBYm42DY5o3ShY=", "39dc201d-df46-4d08-b6bd-0c8aa2e40c14"]
Default: disabled

orgModeMetadataScript (optional)
Script to generate metadata for .org files (if used.) The script receives the title of the note as it's first argument. It must be in your PATH.
Default: "generate-orgmode-metadata"
Fallback text (if no script is available): #+title: $title\n#+date: $(date +%Y-%m-%d)
Simple example script:

#! /bin/sh

title="\$1"
id=$(uuidgen) # org id used by org-roam

cat <<EOF
:PROPERTIES:
:ID: $id
:END:
#+title: $title
#+date: $(date +%Y-%m-%d)
EOF

You can find the latest version of the script that I use in my dotfiles.

For testing

debugging (optional)
Whether to print debug output.
Values: false/true
Default: false

backupMessages (optional)
Whether to keep a copy of all processed messages in $XDG_CACHE_HOME/signal-api-backups.
Values: true/false
Default: true

keepAllAttachments (optional)
Whether to keep attachments on the server after download. false=delete, true=keep
Values: false/true
Default: false