Backend Token Server
Use this guide when a browser or mobile app needs a short-lived Ringnity runtime token. Most customers do not need a new server. They add one small endpoint to the backend they already own.
What you build
Android / iOS / Web app -> customer backend -> Ringnity Server API <- short-lived runtime token <- app uses the token
What stays secret
`RINGNITY_SERVER_API_KEY` stays on the customer backend. Do not put it in Android, iOS, Flutter, React Native, browser bundles, public repositories, or app assets.
RINGNITY_API_BASE_URL=https://api.ringnity.com RINGNITY_SERVER_API_KEY=rn_sk_live_xxx
Step-by-step
- 1Create or renew a Server API key from SDK Variables.
- 2Store the full key in the customer backend secret manager.
- 3Create POST /ringnity/runtime-token in the customer backend.
- 4The endpoint verifies the app user with the customer auth system.
- 5The endpoint calls Ringnity Server API with the secret key.
- 6The app receives only a short-lived runtime token.
Node.js / Express
This is the easiest path when the customer backend uses Node.js. The official Server API helper hides the Ringnity HTTP details.
import express from "express";
import { RingnityServerApi } from "@ringnity/server-api";
const app = express();
app.use(express.json());
const ringnity = RingnityServerApi.create({
apiBaseUrl: process.env.RINGNITY_API_BASE_URL,
apiKey: process.env.RINGNITY_SERVER_API_KEY
});
async function requireCustomer(req) {
// Replace this with your own login/session check.
// Return the customer currently using your app.
return {
id: req.body.userId || "customer-123",
name: req.body.name || "Demo Customer",
email: req.body.email || "customer@example.com"
};
}
app.post("/ringnity/runtime-token", async (req, res) => {
const customer = await requireCustomer(req);
const result = await ringnity.tokens.visitorContext({
visitor: {
externalId: customer.id,
name: customer.name,
email: customer.email
},
metadata: {
source: "android-app"
},
expiresIn: 900
});
// Keep the mobile response simple and consistent.
res.json({
token: result.contextToken,
expiresIn: result.expiresIn,
tokenType: result.tokenType
});
});
app.listen(3000, () => {
console.log("Token server listening on http://localhost:3000");
});Go backend
Go customers can call Ringnity Server API with normal HTTP. No Go SDK is required for the token exchange.
package main
import (
"bytes"
"encoding/json"
"log"
"net/http"
"os"
)
func env(name string, fallback string) string {
value := os.Getenv(name)
if value == "" {
return fallback
}
return value
}
func runtimeToken(w http.ResponseWriter, r *http.Request) {
payload := map[string]any{
"visitor": map[string]any{
"externalId": "customer-123",
"name": "Demo Customer",
"email": "customer@example.com",
},
"metadata": map[string]any{
"source": "android-app",
},
"expiresIn": 900,
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest(
"POST",
env("RINGNITY_API_BASE_URL", "https://api.ringnity.com")+"/api/server/visitor-context-token",
bytes.NewReader(body),
)
req.Header.Set("Authorization", "Bearer "+os.Getenv("RINGNITY_SERVER_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
http.Error(w, "Ringnity request failed", http.StatusBadGateway)
return
}
defer resp.Body.Close()
var ringnity struct {
Success bool `json:"success"`
Data struct {
ContextToken string `json:"contextToken"`
ExpiresIn int `json:"expiresIn"`
TokenType string `json:"tokenType"`
} `json:"data"`
}
json.NewDecoder(resp.Body).Decode(&ringnity)
if resp.StatusCode >= 400 || !ringnity.Success {
http.Error(w, "Ringnity token exchange failed", http.StatusBadGateway)
return
}
json.NewEncoder(w).Encode(map[string]any{
"token": ringnity.Data.ContextToken,
"expiresIn": ringnity.Data.ExpiresIn,
"tokenType": ringnity.Data.TokenType,
})
}
func main() {
http.HandleFunc("/ringnity/runtime-token", runtimeToken)
log.Fatal(http.ListenAndServe(":3000", nil))
}Java Spring Boot
Spring Boot customers can keep the key in application secrets and use `RestClient` to request runtime tokens.
package com.example.demo;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClient;
record RuntimeTokenResponse(String token, Integer expiresIn, String tokenType) {}
@RestController
public class RingnityTokenController {
private final RestClient restClient;
@Value("${ringnity.server-api-key}")
private String serverApiKey;
public RingnityTokenController(
@Value("${ringnity.api-base-url:https://api.ringnity.com}") String apiBaseUrl
) {
this.restClient = RestClient.builder().baseUrl(apiBaseUrl).build();
}
@PostMapping("/ringnity/runtime-token")
public RuntimeTokenResponse runtimeToken() {
// Replace this with your own Spring Security principal/session lookup.
Map<String, Object> body = Map.of(
"visitor", Map.of(
"externalId", "customer-123",
"name", "Demo Customer",
"email", "customer@example.com"
),
"metadata", Map.of("source", "android-app"),
"expiresIn", 900
);
Map<?, ?> response = restClient.post()
.uri("/api/server/visitor-context-token")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + serverApiKey)
.body(body)
.retrieve()
.body(Map.class);
Map<?, ?> data = (Map<?, ?>) response.get("data");
return new RuntimeTokenResponse(
(String) data.get("contextToken"),
(Integer) data.get("expiresIn"),
(String) data.getOrDefault("tokenType", "Bearer")
);
}
}Test the endpoint
curl -X POST "http://localhost:3000/ringnity/runtime-token" \
-H "Content-Type: application/json" \
-d '{
"userId": "customer-123",
"name": "Demo Customer",
"email": "customer@example.com"
}'Expected response
{
"token": "eyJhbGciOi...",
"expiresIn": 900,
"tokenType": "Bearer"
}Use it from Android
Android calls the customer backend endpoint and passes the returned token into the Kotlin SDK. The Android app never sees the Server API key.
suspend fun fetchRingnityToken(): RingnityRuntimeToken {
val response = customerBackend.post("/ringnity/runtime-token")
return RingnityRuntimeToken(
token = response.token,
expiresIn = response.expiresIn,
tokenType = response.tokenType ?: "Bearer"
)
}
val ringnity = Ringnity.create(
RingnityConfig(
tokenProvider = ::fetchRingnityToken,
callMode = RingnityCallMode.BASIC
)
)Production checklist
- Verify the customer's own user session before issuing a token.
- Use HTTPS only.
- Keep token expiry short, usually 900 seconds.
- Log failures without printing `RINGNITY_SERVER_API_KEY` or runtime tokens.
- Rotate the Server API key if it was copied into the wrong place.
Turn Your Website Into a Real-Time Call Center
Let customers call your team directly from your website, no phone numbers and no apps required. Just add one <script>.
