API: Sign requests (V2)
A V2 API signature covers the entire request envelope — method, path, query, signed headers, and body hash — not just the URL. Include the signature in request headers.
Request headers
Authorization: YOUR_API_KEY
x-onramper-signature: v2:{base64_signature}
x-onramper-timestamp: 2024-01-15T10:30:00.000Z
x-onramper-nonce: 550e8400-e29b-41d4-a716-446655440000
x-onramper-timestampandx-onramper-nonceare sent as headers but go into lines 2–3 of the canonical string — not the signed headers section (line 7).
The canonical string
The same 8-line format as widget signing, with API-specific values:
| Line | Component | Example |
|---|---|---|
| 1 | Version | ONRAMPER-SIG-V2 |
| 2 | Timestamp | 2024-01-15T10:30:00.000Z |
| 3 | Nonce | 550e8400-e29b-41d4-... |
| 4 | HTTP method (uppercase) | POST |
| 5 | Request path | /checkout/intent |
| 6 | Canonical query (sorted A–Z; empty if none) | amount=100¤cy=USD |
| 7 | Canonical headers | authorization:YOUR_API_KEY |
| 8 | Body hash (SHA256 hex) | e3b0c44... |
Canonical headers (line 7): only two headers are ever signed, in alphabetical order, as name:value (lowercase name, trimmed value), joined by \n:
authorization— always requiredcontent-type— only if present in the request
Body hash (line 8):
| Body type | Handling |
|---|---|
| Empty body (e.g. GET) | SHA256 of empty string: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 |
| JSON body | Canonicalize per RFC 8785 (sorted keys, no whitespace), then SHA256 |
| Non-JSON string | SHA256 of the string as-is |
Securing endpoints on your side
The proposed flow is:
- Frontend → Backend: your frontend calls your backend.
- Backend: authenticate the end user, validate the request and sign the request.
- Backend → Onramper: send the signed request to Onramper.
- Backend → Frontend: return the result to your frontend.
Your frontend → backend call should always include additional controls such as:
- User authentication (prove who is making the request)
- Request validation / matching (e.g., the user is allowed to use the specified wallet address)
- Rate limiting to prevent burst/abusive checkout attempts per user
This is especially important for flows that end up calling Onramper’s /checkout endpoints and we will audit your integration for this before going live.
Example: signed GET request
curl -X GET "https://api.onramper.com/quotes/usd/btc?amount=100" \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "x-onramper-signature: v2:MEUCIQDKZf3L8gT2x..." \
-H "x-onramper-timestamp: 2024-01-15T10:30:00.000Z" \
-H "x-onramper-nonce: 550e8400-e29b-41d4-a716-446655440000"Complete TypeScript example
import { createHash, createPrivateKey, randomUUID, sign } from 'crypto';
import canonicalize from 'canonicalize'; // npm i canonicalize — RFC 8785
function sha256(data: string): string {
return createHash('sha256').update(data).digest('hex');
}
function signApiRequestV2(options: {
method: string;
path: string;
apiKey: string;
privateKeyPem: string;
queryParams?: Record<string, string>;
body?: object;
contentType?: string;
}): Record<string, string> {
const { method, path, apiKey, privateKeyPem, queryParams, body, contentType } = options;
const timestamp = new Date().toISOString();
const nonce = randomUUID();
const canonicalQuery = queryParams
? (() => { const p = new URLSearchParams(queryParams); p.sort(); return p.toString(); })()
: '';
const headerParts = [`authorization:${apiKey}`];
if (contentType) headerParts.push(`content-type:${contentType}`);
const canonicalHeaders = headerParts.join('\n');
const bodyHash = body ? sha256(canonicalize(body) ?? '') : sha256('');
const contentToSign = [
'ONRAMPER-SIG-V2',
timestamp,
nonce,
method.toUpperCase(),
path,
canonicalQuery,
canonicalHeaders,
bodyHash,
].join('\n');
const privateKey = createPrivateKey(privateKeyPem);
const signature = sign(null, Buffer.from(contentToSign), privateKey);
return {
'Authorization': apiKey,
'x-onramper-signature': `v2:${signature.toString('base64')}`,
'x-onramper-timestamp': timestamp,
'x-onramper-nonce': nonce,
};
}
