Skip to content

Getting started

Choose one installation method.

Install the app and CLI together with Homebrew:

Terminal window
brew install --cask luxass/tap/imessage-relay

Or choose a version from the latest release. Replace X.Y.Z below with that version, without the leading v:

Terminal window
version="X.Y.Z"
curl -fsSLO \
"https://github.com/luxass/imessage-relay/releases/download/v${version}/imessage-relay-${version}-macos-universal.zip"
unzip "imessage-relay-${version}-macos-universal.zip"
mv "iMessage Relay.app" /Applications/
open "/Applications/iMessage Relay.app"

The universal app requires macOS 14 or newer. Releases are signed with Developer ID and notarized by Apple. To start the app at login, enable Launch at login in Settings after installing it in /Applications.

The cask also installs the imessage-relay command. Show the version and available options with:

Terminal window
imessage-relay --version
imessage-relay --help

To use the CLI from the ZIP, run ./imessage-relay from the extracted directory. Keep the .bundle resource directories beside the executable. The CLI does not require the app to be open.

The standalone imessage-relay-cli formula is deprecated from November 1, 2026. To switch from that formula to the combined release, run:

Terminal window
brew uninstall --formula imessage-relay-cli
brew install --cask luxass/tap/imessage-relay

A release candidate is a preview build for testing before a stable release. Quit the relay before switching versions. If the stable cask is installed, uninstall it with brew uninstall --cask luxass/tap/imessage-relay. If the standalone CLI formula is installed, uninstall it with brew uninstall --formula luxass/tap/imessage-relay-cli. Then run:

Terminal window
brew install --cask luxass/tap/imessage-relay@rc
imessage-relay --version
imessage-relay --help

The RC cask installs both the app and CLI. It cannot be installed alongside the stable cask because they install the same app and command. Both use the same configuration. To return to stable, uninstall imessage-relay@rc and install imessage-relay. Do not use --zap when switching if you want to keep the app configuration.

For the app, choose an API token during setup and review the macOS permissions. Grant iMessage Relay Full Disk Access in System Settings > Privacy & Security. If macOS closes the app, reopen it to return to the Permissions step. Finish setup, then open Settings > Status from the menu bar. Under API Token, choose Reveal, then Copy. Export the copied value in the terminal where you run the examples:

Terminal window
export RELAY_TOKEN='paste-the-copied-token-here'

If you generate a token, the app uses 256 bits of randomness and stores it in your login Keychain. It creates its non-secret settings at ~/Library/Application Support/imessage-relay/config.json. Add your local account ID and send allowlist there, then choose Reload under Settings > Status > Configuration. An empty allowlist denies every send.

{
"allowRemoteConnections": false,
"allowedRecipients": ["person@example.com", "+1 202 555 0123"],
"hostname": "127.0.0.1",
"maximumMediaBytes": 26214400,
"port": 8080,
"senderAccountID": "your-local-imessage-account-id"
}

If you use the CLI, grant Full Disk Access to its terminal or service and start it with environment variables. For a Homebrew installation, use imessage-relay instead of ./imessage-relay:

Terminal window
export RELAY_TOKEN='replace-with-a-long-random-token'
export RELAY_SENDER_ACCOUNT_ID='your-local-imessage-account-id'
export RELAY_ALLOWED_RECIPIENTS='person@example.com,+1 202 555 0123'
./imessage-relay

The first send can prompt for Automation > Messages permission. GET /v1/status and GET /v1/sender never trigger this prompt.

From another terminal, check that the relay can read the Messages database:

Terminal window
curl -s localhost:8080/v1/status \
-H "Authorization: Bearer $RELAY_TOKEN" | jq
curl -s 'localhost:8080/v1/conversations?limit=3' \
-H "Authorization: Bearer $RELAY_TOKEN" | jq '.items'

Filter by an exact phone number or email address to find its conversations:

Terminal window
curl -sG localhost:8080/v1/conversations \
-H "Authorization: Bearer $RELAY_TOKEN" \
--data-urlencode 'participant=person@example.com' | jq '.items'

Use a conversation id to read its messages:

Terminal window
curl -s 'localhost:8080/v1/conversations/CONVERSATION_ID/messages?limit=10' \
-H "Authorization: Bearer $RELAY_TOKEN" | jq

Run this request only when you started the relay with an allowlist:

Terminal window
curl -s -X POST localhost:8080/v1/messages \
-H "Authorization: Bearer $RELAY_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: first-message' \
-d '{"to":"person@example.com","text":"hello"}' | jq

The response contains request_id, correlation_status, messages, media, and poll_url. Repeat the same request with the same idempotency key to receive the stored response without another send. Use a new key for a different message.

The relay waits up to three seconds for the matching Messages rows. Request poll_url while correlation_status is pending or partial. A send with text and media can produce separate provider messages. The messages array reports each message and its current lifecycle status.

You can upload a file for later use:

Terminal window
curl -s -X POST localhost:8080/v1/media \
-H "Authorization: Bearer $RELAY_TOKEN" \
-H 'Content-Type: image/jpeg' \
-H 'X-Filename: photo.jpg' \
--data-binary @photo.jpg | jq

To send the returned media.media_id, use a message request with an existing conversation_id. Attachment and reply sends require Accessibility permission for the relay process.

See the API reference for all endpoints and response details.