Skip to content
Go to file


Failed to load latest commit information.
Latest commit message
Commit time

FCM/APNs Push Relay

CI License

This server accepts push requests via HTTP and notifies the Google FCM / Apple APNs push services.

Request Format

  • POST request to /push
  • Request body must use application/x-www-form-urlencoded encoding

Request keys:

  • type: Either fcm or apns
  • token: The device push token
  • session: SHA256 hash of public permanent key of the initiator
  • version: Threema Web protocol version
  • affiliation (optional): An identifier for affiliating consecutive pushes
  • ttl (optional): The lifespan of a push message, defaults to 90 seconds
  • collapse_key: (optional) A parameter identifying a group of push messages that can be collapsed.
  • bundleid (APNs only): The bundle id to use
  • endpoint (APNs only): Either p (production) or s (sandbox)


curl -X POST -H "Origin: https://localhost" localhost:3000/push \
    -d "type=fcm&token=asdf&session=123deadbeef&version=3"
curl -X POST -H "Origin: https://localhost" localhost:3000/push \
    -d "type=apns&token=asdf&session=123deadbeef&version=3&"

Possible response codes:

  • HTTP 204 (No Content): Request was processed successfully
  • HTTP 400 (Bad Request): Invalid or missing POST parameters (including expired push tokens)
  • HTTP 500 (Internal Server Error): Processing of push request failed on the Push Relay server
  • HTTP 502 (Bad Gateway): Processing of push request failed on the FCM or APNs server

Push Payload

The payload format looks like this:

  • wcs: Webclient session (sha256 hash of the public permanent key of the initiator), string
  • wca: An optional identifier for affiliating consecutive pushes, string or null
  • wct: Unix epoch timestamp of the request in seconds, i64
  • wcv: Protocol version, u16


The FCM message contains the payload data as specified above.


The APNs message contains a key "3mw" containing the payload data as specified above.


You need the Rust compiler (1.49 or higher). First, create a config.ini file that looks like this:

api_key = "your-api-key"

keyfile = "your-keyfile.p8"
key_id = "AB123456XY"
team_id = "CD987654YZ"

If you want to log the pushes to InfluxDB, add the following section:

connection_string = ""
user = "foo"
pass = "bar"
db = "baz"

Then simply run

export RUST_LOG=push_relay=debug,hyper=info,a2=info
cargo run build and start the server in debug mode.


  • Always create a build in release mode: cargo build --release
  • Use a reverse proxy with proper TLS termination (e.g. Nginx)
  • Set RUST_LOG=push_relay=info,hyper=info,a2=info env variable


To run tests:

cargo test


To run lints:

$ rustup component add clippy
$ cargo clean && cargo clippy --all-targets


Licensed under either of

at your option.