Verifying responses
Orbita signs its responses to your signed requests with its own RSA key. Verifying the signature proves a response really came from Orbita and wasn't changed in transit.
1. Get Orbita's public key
Fetch it from Get Orbita's public key — no authentication needed. Cache it, and refresh it periodically (or when verification starts failing) so you pick up a key rotation.
curl "{baseUrl}/api/public-key"2. Read the signature headers
3. Rebuild the signed string
Join these five values with a newline (\n) — the same shape as a request signature, plus the status code:
200. This stops a success response being swapped for an error, or vice versa.X-Response-Signature-Timestamp.4. Verify
Verify the signature with Orbita's public key, and reject any response whose timestamp is more than 5 minutes from your clock. The timestamp only limits how old a replayed response can be — it doesn't prove the response is current. The signature isn't tied to your specific request, so a captured response (for example, a payout-status GET) can be replayed against an identical request within those 5 minutes. Treat a verified status as true as of its timestamp, not necessarily now.
const crypto = require("crypto");
const MAX_AGE_MS = 5 * 60 * 1000;
// res: the fetch Response; bodyText: await res.text() -- the exact bytes received.
function verifyResponse(method, pathWithQuery, res, bodyText, orbitaPublicKeyPem) {
const signature = res.headers.get("x-response-signature");
const timestampMs = res.headers.get("x-response-signature-timestamp");
if (!signature || !timestampMs) return false;
const ts = Number(timestampMs);
if (!Number.isFinite(ts) || Math.abs(Date.now() - ts) > MAX_AGE_MS) return false;
const bodyHash = crypto.createHash("sha256").update(bodyText).digest("hex");
const payload = [method, pathWithQuery, String(res.status), timestampMs, bodyHash].join("\n");
return crypto
.createVerify("RSA-SHA256")
.update(payload)
.verify(orbitaPublicKeyPem, signature, "base64");
}- Only responses to requests that passed signature verification are signed. Get Orbita's public key itself is never signed.
- If a response you expect to be signed arrives without signature headers, fails verification, or never arrives, don't act on it. Your request may still have been processed, so follow Retrying safely: for conversions, check List conversions before sending the request again, since a retry can create a duplicate; for payouts, retry with the original
customerReference.
