Skip to main content
Every authenticated request to the Finrock API requires two headers: a signed JWT token that proves you hold the private key registered against your account, and a static API key that identifies your API user. Neither header alone is sufficient — both must be present and valid for a request to succeed.

Required Headers

Attach the following two headers to every authenticated API call:

Step 1: Generate an RSA-4096 Key Pair

Finrock requires RSA keys with a minimum length of 4096 bits. Generate your key pair with OpenSSL:
This produces two files:
  • private_key.pem — your signing key; you keep this secret and use it to sign JWTs
  • public_key.pem — the key you upload to the Finrock control panel when creating an API User
Never share or commit your private_key.pem. Store it in a dedicated secrets manager such as AWS Secrets Manager, HashiCorp Vault, or Azure Key Vault. Restrict read access to only the process or service that signs requests.

Step 2: Register Your Public Key

Log in to the Finrock control panel and navigate to API Users. Create a new API User and upload the contents of public_key.pem. Finrock stores your public key and associates it with the API key UUID that is generated. Copy this UUID — you will use it as both the x-api-key header value and the sub claim in your JWT payload.

Step 3: Build the JWT Payload

Each JWT you send must be freshly generated and scoped to the specific request it authenticates. The JWT payload must include all of the following fields:
The exp field must be strictly less than iat + 30 seconds. Any JWT with an expiry window of 30 seconds or more will be rejected. Keep the window short — 25 seconds is a safe default — to limit the replay window if a token is intercepted.

Computing bodyHash

The bodyHash field is the lowercase hex-encoded SHA-256 hash of the raw request body string, exactly as it will be sent over the wire. For GET requests or requests with no body, hash an empty string. Example body:
Resulting SHA-256 bodyHash:
Compute the hash on the exact byte string you send as the request body, before any encoding or transformation. Do not pretty-print or reorder JSON fields between computing the hash and sending the body.

Step 4: Sign the JWT

Sign the payload with your RSA private key using the RS256 algorithm (RSASSA-PKCS1-v1_5 with SHA-256). The signed JWT is a standard three-part base64url-encoded string. Pass it in the Authorization header as Bearer <JWT>.

Code Examples

The following examples show how to build and sign the JWT in several languages. Full, runnable source files are available in the official snippet repository at github.com/gofinrock/jwt-snippets.

Complete Request Example

The following shows a fully authenticated curl request that creates a BTC wallet, illustrating how all authentication pieces come together:

Authentication Checklist

Use this checklist to verify your implementation before going to production:
  • RSA key pair generated with a minimum of 4096 bits
  • Public key uploaded to the Finrock control panel and API key UUID copied
  • Private key stored in a secrets manager, not in source code or environment variables in plain text
  • JWT payload includes all six required fields: uri, nonce, iat, exp, sub, bodyHash
  • exp is set to less than iat + 30 seconds
  • nonce is unique per request (UUID v4 recommended)
  • bodyHash is computed from the exact raw body string sent in the request
  • JWT is signed with RS256 (not HS256 or any other algorithm)
  • Both Authorization: Bearer <JWT> and x-api-key headers are present on every authenticated request
  • JWT is regenerated fresh for each request — do not reuse tokens

Troubleshooting

Check the following in order:
  1. Confirm x-api-key matches the UUID shown in the control panel for your API User.
  2. Verify the public key uploaded to the control panel corresponds to the private key you are using to sign.
  3. Ensure exp is less than iat + 30. Clocks that are out of sync can cause valid-looking tokens to be rejected.
  4. Confirm bodyHash was computed from the exact bytes sent as the request body. Even a single whitespace difference will produce a different hash.
  5. Verify the JWT algorithm header is RS256, not HS256.
Each request must use a unique nonce value. Using a UUID v4 generated fresh for each request is the simplest approach. Do not copy nonces from previous requests or log them for reuse.