gRPC API
The gRPC API links and unlinks platform accounts and syncs ephemeral key pools. The RelaySMS app calls it while online.
Protocol buffer file
curl -O -L https://raw.githubusercontent.com/smswithoutborders/RelaySMS-Publisher/main/protos/v3/publisher.proto
Package publisher.v3, service Publisher.
Generate a client
Generate stubs with protoc for your language. For Python:
pip install grpcio-tools
python -m grpc_tools.protoc -I . --python_out=. --grpc_python_out=. publisher.proto
Request headers
Every RPC needs these headers. Headers ending in -bin carry raw binary.
| Header | Type | Required | Description |
|---|---|---|---|
x-payload-bin | bytes | Yes | Encrypted request payload ciphertext |
x-public-key-bin | bytes | Yes | Client ephemeral public key ($eC_{pk}$, 32 bytes) |
x-key-id | string | Yes | ID of the server identity key used to encrypt the payload |
x-nonce-bin | bytes | Yes | Request nonce (16 bytes) |
x-timestamp | string | Yes | Unix timestamp in seconds, within NONCE_TTL_SECONDS of the server’s clock |
The examples use -plaintext against a local server; drop it for a server behind TLS on port 443.
Methods
Get OAuth2 authorization URL
Returns the URL that starts the OAuth2 flow.
Request: GetOAuth2AuthorizationUrlRequest
| Field | Type | Required | Description |
|---|---|---|---|
| platform | string | Yes | Platform identifier, such as gmail |
| state | string | No | OAuth2 state, returned unchanged |
| code_verifier | string | No | PKCE code verifier |
| autogenerate_code_verifier | bool | No | Generate a code verifier when none is sent |
| redirect_url | string | No | Redirect URL for OAuth2 application |
| request_identifier | string | No | Request identifier for tracking |
Response: GetOAuth2AuthorizationUrlResponse
| Field | Type | Description |
|---|---|---|
| authorization_url | string | Generated authorization URL |
| state | string | The state sent |
| code_verifier | string | PKCE code verifier used |
| message | string | Human-readable result |
| scope | string | Authorization request scope |
| client_id | string | OAuth2 application client ID |
| redirect_url | string | OAuth2 application redirect URL |
Example:
grpcurl -plaintext \
-H 'x-payload-bin: <ENCRYPTED_PAYLOAD>' \
-H 'x-public-key-bin: <CLIENT_PUBLIC_KEY>' \
-H 'x-key-id: <KEY_ID>' \
-H 'x-nonce-bin: <NONCE>' \
-H 'x-timestamp: <TIMESTAMP>' \
-d '{
"platform": "gmail",
"autogenerate_code_verifier": true
}' \
-proto publisher.proto \
<HOST>:<PORT> publisher.v3.Publisher/GetOAuth2AuthorizationUrl
Exchange OAuth2 code and store token
Exchanges an OAuth2 authorization code for tokens and stores them. Returns encrypted token data and a set of server ephemeral keys.
Request: ExchangeOAuth2CodeAndStoreRequest
| Field | Type | Required | Description |
|---|---|---|---|
| platform | string | Yes | Platform identifier |
| authorization_code | string | Yes | OAuth2 authorization code |
| code_verifier | string | No | Code verifier for PKCE |
| redirect_url | string | No | Redirect URL for OAuth2 application |
| request_identifier | string | No | Request identifier for tracking |
| client_ephemeral_public_keys | list[PublicKey] | Yes | Exactly 256 ephemeral public keys generated by the client, with key_id 0-255 each used once |
PublicKey message:
| Field | Type | Description |
|---|---|---|
| key_id | uint32 | Key index (0-255) |
| public_key | bytes | 32-byte public key |
Response: ExchangeOAuth2CodeAndStoreResponse
| Field | Type | Description |
|---|---|---|
| success | bool | true if the call succeeded |
| message | string | Human-readable result |
| account_identifier | string | Account identifier, such as mail@example.com |
| token_ciphertext | bytes | Encrypted token ciphertext |
| token_id | uint32 | Unique identifier for the stored token |
| server_ephemeral_public_keys | list[PublicKey] | Exactly 256 ephemeral public keys generated by the server |
Example:
grpcurl -plaintext \
-H 'x-payload-bin: <ENCRYPTED_PAYLOAD>' \
-H 'x-public-key-bin: <CLIENT_PUBLIC_KEY>' \
-H 'x-key-id: <KEY_ID>' \
-H 'x-nonce-bin: <NONCE>' \
-H 'x-timestamp: <TIMESTAMP>' \
-d @ \
-proto publisher.proto \
<HOST>:<PORT> publisher.v3.Publisher/ExchangeOAuth2CodeAndStore <<EOF
{
"platform": "gmail",
"authorization_code": "auth_code",
"client_ephemeral_public_keys": [
{"key_id": 0, "public_key": "..."},
... 254 more keys ...
{"key_id": 255, "public_key": "..."}
]
}
EOF
Revoke and delete OAuth2 token
Revokes and deletes a stored OAuth2 token, identified by its token ID and the key ID that encrypted it.
Request: RevokeOAuth2TokenRequest
| Field | Type | Required | Description |
|---|---|---|---|
| token_id | uint32 | Yes | Unique identifier for the stored token |
| key_id | uint32 | Yes | Key index used for encrypting the token |
Response: RevokeOAuth2TokenResponse
| Field | Type | Description |
|---|---|---|
| success | bool | true if the call succeeded |
| message | string | Human-readable result |
Example:
grpcurl -plaintext \
-H 'x-payload-bin: <ENCRYPTED_PAYLOAD>' \
-H 'x-public-key-bin: <CLIENT_PUBLIC_KEY>' \
-H 'x-key-id: <KEY_ID>' \
-H 'x-nonce-bin: <NONCE>' \
-H 'x-timestamp: <TIMESTAMP>' \
-d '{
"token_id": 2941967295,
"key_id": 123
}' \
-proto publisher.proto \
<HOST>:<PORT> publisher.v3.Publisher/RevokeOAuth2Token
Get PNBA code
Requests a one-time passcode (OTP) for phone number-based authentication (PNBA).
Request: GetPNBACodeRequest
| Field | Type | Required | Description |
|---|---|---|---|
| platform | string | Yes | Platform identifier |
| phone_number | string | Yes | Phone number to receive OTP |
| request_identifier | string | No | Request identifier for tracking |
| channel | string | No | Channel to send the code over, such as signal |
Response: GetPNBACodeResponse
| Field | Type | Description |
|---|---|---|
| success | bool | true if the call succeeded |
| message | string | Human-readable result |
| expires_at | int64 | Optional. Code expiry, Unix epoch seconds |
Example:
grpcurl -plaintext \
-H 'x-payload-bin: <ENCRYPTED_PAYLOAD>' \
-H 'x-public-key-bin: <CLIENT_PUBLIC_KEY>' \
-H 'x-key-id: <KEY_ID>' \
-H 'x-nonce-bin: <NONCE>' \
-H 'x-timestamp: <TIMESTAMP>' \
-d '{
"platform": "telegram",
"phone_number": "+237600000000"
}' \
-proto publisher.proto \
<HOST>:<PORT> publisher.v3.Publisher/GetPNBACode
Exchange PNBA code and store token
Exchanges a one-time passcode (OTP) for a token, stores it, and returns encrypted token data.
Request: ExchangePNBACodeAndStoreRequest
| Field | Type | Required | Description |
|---|---|---|---|
| platform | string | Yes | Platform identifier |
| phone_number | string | Yes | Phone number that received OTP |
| authorization_code | string | Yes | PNBA authorization code |
| password | string | No | Password for two-step verification |
| request_identifier | string | No | Request identifier for tracking |
| client_ephemeral_public_keys | list[PublicKey] | Yes | Exactly 256 ephemeral public keys generated by the client, with key_id 0-255 each used once |
| channel | string | No | Channel the code was sent over, such as signal |
Response: ExchangePNBACodeAndStoreResponse
| Field | Type | Description |
|---|---|---|
| success | bool | true if the call succeeded |
| message | string | Human-readable result |
| two_step_verification_enabled | bool | Two-step verification status |
| account_identifier | string | Account identifier |
| token_ciphertext | bytes | Encrypted token ciphertext |
| token_id | uint32 | Unique identifier for the stored token |
| server_ephemeral_public_keys | list[PublicKey] | Exactly 256 ephemeral public keys generated by the server |
| key_id | uint32 | Key ID used for encrypting token |
Example:
grpcurl -plaintext \
-H 'x-payload-bin: <ENCRYPTED_PAYLOAD>' \
-H 'x-public-key-bin: <CLIENT_PUBLIC_KEY>' \
-H 'x-key-id: <KEY_ID>' \
-H 'x-nonce-bin: <NONCE>' \
-H 'x-timestamp: <TIMESTAMP>' \
-d @ \
-proto publisher.proto \
<HOST>:<PORT> publisher.v3.Publisher/ExchangePNBACodeAndStore <<EOF
{
"platform": "telegram",
"phone_number": "+237600000000",
"authorization_code": "auth_code",
"client_ephemeral_public_keys": [
{"key_id": 0, "public_key": "..."},
... 254 more keys ...
{"key_id": 255, "public_key": "..."}
]
}
EOF
Revoke and delete PNBA token
Revokes and deletes a stored PNBA token.
Request: RevokePNBATokenRequest
| Field | Type | Required | Description |
|---|---|---|---|
| token_id | uint32 | Yes | Unique identifier for the stored token |
| key_id | uint32 | Yes | Key index used for encrypting the token |
Response: RevokePNBATokenResponse
| Field | Type | Description |
|---|---|---|
| success | bool | true if the call succeeded |
| message | string | Human-readable result |
Example:
grpcurl -plaintext \
-H 'x-payload-bin: <ENCRYPTED_PAYLOAD>' \
-H 'x-public-key-bin: <CLIENT_PUBLIC_KEY>' \
-H 'x-key-id: <KEY_ID>' \
-H 'x-nonce-bin: <NONCE>' \
-H 'x-timestamp: <TIMESTAMP>' \
-d '{
"token_id": 2941967295,
"key_id": 123
}' \
-proto publisher.proto \
<HOST>:<PORT> publisher.v3.Publisher/RevokePNBAToken
Sync keys
Replaces a token’s key pools: the client sends 256 new ephemeral public keys and the server returns 256 of its own.
Request: SyncKeysRequest
| Field | Type | Required | Description |
|---|---|---|---|
| token_id | uint32 | Yes | Unique identifier for the stored token |
| key_id | uint32 | Yes | The current key index used for encrypting the token |
| client_ephemeral_public_keys | list[PublicKey] | Yes | Exactly 256 new ephemeral public keys generated by the client, with key_id 0-255 each used once |
Response: SyncKeysResponse
| Field | Type | Description |
|---|---|---|
| success | bool | true if the call succeeded |
| message | string | Human-readable result |
| server_ephemeral_public_keys | list[PublicKey] | Exactly 256 ephemeral public keys generated by the server |
Example:
grpcurl -plaintext \
-H 'x-payload-bin: <ENCRYPTED_PAYLOAD>' \
-H 'x-public-key-bin: <CLIENT_PUBLIC_KEY>' \
-H 'x-key-id: <KEY_ID>' \
-H 'x-nonce-bin: <NONCE>' \
-H 'x-timestamp: <TIMESTAMP>' \
-d @ \
-proto publisher.proto \
<HOST>:<PORT> publisher.v3.Publisher/SyncKeys <<EOF
{
"token_id": 2941967295,
"key_id": 123,
"client_ephemeral_public_keys": [
{"key_id": 0, "public_key": "..."},
... 254 more keys ...
{"key_id": 255, "public_key": "..."}
]
}
EOF
Every RPC returns a standard gRPC status code; 0 OK is success.