Authentication
Pave Bank API Authentication
To access the Pave Bank API, use Bearer Token Authentication via the OAuth 2.0 Client Credentials Flow. Follow these steps:
client_id and client_secretExchange them for an access token by making a POST request to: {baseUrl}/oauth2/token
Include the access token in the Authorization header of your API
requests
Security Note
Keep your client_id and client_secret secure. Never expose them in
public code. If compromised, rotate your credentials immediately.
Token Request Example
Make a POST request with the following body: grant_type=client_credentials&audience=developer
Use HTTP Basic Auth with your client_id and client_secret
Authenticated API Call
Roles
Each API credential is assigned a role that controls what actions it can perform. The role is set when the credential is created.
| Role | Description |
|---|---|
admin | Full read and write access. Can perform all API operations including transfers, account creation, and webhook configuration. |
viewer | Read-only access. Can view accounts, balances, transactions, and other resources but cannot create or modify them. |
Each API endpoint displays a role badge indicating the minimum role required. See individual endpoint pages for details.
If a viewer credential attempts a write operation, the API returns a
PERMISSION_DENIED error with the message: "user does not have permission to perform this action".
Request Payload Signing
For mutating API requests (POST, PUT, PATCH, DELETE), Pave Bank supports request payload signing as a JSON Web Token (JWT). Signing provides request integrity, non-repudiation, and SCA compliance alongside your OAuth token.
The signed request carries one extra header — Pave-Bank-Request-Signature — which is a JWT covering the HTTP method, request URI, body hash, and replay-protection claims.
Verification Modes
Each legal entity operates in one of two modes:
| Mode | Behavior |
|---|---|
| Permissive | Default. Requests succeed regardless of signing. Failures are recorded in the customer portal under Verification Logs so you can debug your integration. |
| Enforced | All mutating requests must include a valid signature. Unsigned or invalid requests are rejected with 401 Unauthorized. |
Permissive mode lets you integrate signing incrementally — verify your client implementation against real failures before flipping the switch.
Irreversible Signing Enforcement
Once your legal entity enables signing enforcement, it cannot be reversed. All mutating requests must be signed. Ensure all integrations are updated before enabling.
Setup
Generate a key pair using one of the supported algorithms (see below). Up to 2 active keys per credential.
Register the public key via the Pave Bank portal. You will receive a
key ID (kid) — keep it; the JWT must reference it in its header.
Build a JWT carrying the request claims and sign it with your private
key. Send it as Pave-Bank-Request-Signature.
Supported Algorithms
EdDSA(Ed25519) — recommendedRS256,RS384,RS512(RSA ≥ 2048-bit)PS256(RSA-PSS, ≥ 2048-bit)
Pick one algorithm at registration time. The same RSA key cannot be reused for RS256 and PS256 — register two separate entries if you want both.
Signing Header
| Header | Description |
|---|---|
Pave-Bank-Request-Signature | The signed JWT. |
JWT Header
| Field | Required | Purpose |
|---|---|---|
alg | yes | Must match the algorithm of the registered key. |
typ | yes | Always "JWT". |
kid | yes | Key ID returned at registration. Echo it back as-is. |
JWT Payload
| Claim | Required | Purpose |
|---|---|---|
iss | yes | Your OAuth client ID. Must equal the bearer token's client. |
iat | yes | Issued-at (Unix seconds). |
exp | yes | Expiry (Unix seconds). Lifetime ≤ 5 minutes. |
jti | yes | Unique nonce (UUID, ULID, or any unique string ≤ 128 chars). |
method | yes | HTTP method, uppercase. Must match the actual request. |
uri | yes | Request URI exactly as sent (no normalization). Must match the request. |
body_hash | yes | Lowercase hex SHA-256 of the raw request body bytes. |
Signed Request Example
Important: Raw Body Hash
The body_hash claim must be the SHA-256 hex digest of the exact raw request body bytes sent over the wire.
Do not parse the JSON and re-serialize it before hashing — any transformation (added whitespace, key reordering, number formatting) will change the byte representation and cause a hash mismatch.
Implementation Examples
Key management
Key generation is not provided in these examples. Register the
public key via the Pave Bank portal to obtain a kid, and keep the
private key in secure storage.
Verification Reason Codes
In enforced mode, a non-passing verification returns 401 Unauthorized with the reason code as the error message. In permissive mode, the request succeeds but the failure is recorded in Verification Logs (see below) with the same reason code.
| Reason | Description |
|---|---|
missing | Pave-Bank-Request-Signature header was not provided |
malformed | JWT could not be parsed |
nonce_missing | jti claim missing |
nonce_malformed | jti claim too long (>128 chars) |
body_hash_mismatch | body_hash claim does not match the actual request body |
timestamp_skew | iat / exp outside the allowed window |
method_mismatch | method claim does not match the HTTP method |
uri_mismatch | uri claim does not match the request URI |
algorithm_mismatch | JWT alg does not match the registered key's algorithm |
signature_mismatch | The signature did not verify against the public key |
replay_detected | The jti was already used |
expired | exp missing or lifetime > 5 minutes |
Response Headers (permissive mode)
In permissive mode, every response to a mutating request carries headers describing the signature verification outcome. Use these to confirm your signing implementation before enabling enforcement.
| Header | Values | Description |
|---|---|---|
Pave-Bank-Signature-Verification | passed, failed | Whether the signature verified successfully. |
Pave-Bank-Signature-Reason | reason code (see table above) | Set only when verification failed. |
Pave-Bank-Signature-Mode | permissive | Set only when verification failed. |
Successful verification example:
Failed verification example (request still goes through):
In enforced mode
Failed requests are rejected with 401 Unauthorized and the reason is returned in the response body, not as a header.
Verification Logs
You can review verification failures for any developer API credential in the customer portal under Settings → Developer API → [credential] → Verification Logs.
The page lists every failed attempt with the request path, signing key used, JWT algorithm, failure reason, and timestamp. The timestamp column defaults to your local timezone and can be toggled to UTC from the column header. You can filter by signing key, date range, request path, and failure reason — useful when iterating in permissive mode before enforcement.
Key Rotation
To rotate with zero downtime: register a new key (max 2 active), switch your signing code, then revoke the old key.