Signing Payload Format
Every request to the OpenFX API must include an Ed25519 signature computed over a signing payload — a deterministic string built from four components of the request, joined by newline characters:| Component | Description | Example |
|---|---|---|
HTTP_VERB | Uppercase HTTP method | GET, POST, PATCH, DELETE |
REQUEST_PATH | Full path including query string, without host | /v1/entities?limit=10 |
TIMESTAMP | Unix timestamp in seconds (same value sent in X-Timestamp) | 1740500000 |
BODY | Raw JSON request body, or empty string for requests with no body | {"type":"individual","fullName":"Jane Doe"} |
The
REQUEST_PATH includes the query string if present. For POST requests to
/v1/payments, the path is simply /v1/payments. For GET requests with query
parameters like /v1/entities?limit=10&starting_after=ent_01953e1a, the entire
path plus query string is included.Step-by-Step Process
Follow these six steps for every API request:Step 1: Build the Signing Payload
Concatenate the four components with newline (\n) separators:
GET requests with no body, the payload ends with an empty string after the final newline:
Step 2: Sign with Your Ed25519 Private Key
Use your Ed25519 private key to sign the UTF-8 encoded bytes of the signing payload. Ed25519 produces a deterministic 64-byte signature.Step 3: Base64-Encode the Signature
Encode the 64-byte raw signature using standard Base64 encoding (not URL-safe Base64).Step 4: Set the X-Signature Header
Place the Base64-encoded signature in the X-Signature request header.
Step 5: Set the X-Timestamp Header
Set X-Timestamp to the same Unix timestamp (seconds) used in the signing payload.
Step 6: Set the Authorization Header
Include your API key as a Bearer token: Authorization: Bearer <api_key>.
Complete Code Examples
GET Request (No Body)
Fetching a list of entities — the signing payload has an empty body component.POST Request (With JSON Body)
Creating a payment — the signing payload includes the full JSON body.Signing Payload Examples
To make the signing payload construction concrete, here are exact payload strings for common request types:GET with Query Parameters
Note the trailing newline followed by an empty string. For
GET requests, the body
component is empty, but the newline separator before it is still present.POST with JSON Body
PATCH with Partial Update
DELETE (No Body)
Timestamp Tolerance
The OpenFX server rejects any request where theX-Timestamp value differs from the
server’s current time by more than 60 seconds in either direction.
Common Pitfalls
1. Body Mismatch Between Signing and Sending
The most common signing failure is signing one serialization of the JSON body but sending a different one. This happens when you:- Use a language’s
json=parameter (which re-serializes) instead of sending the pre-serializeddata=string - Add or remove whitespace between signing and sending
- Reorder JSON keys between signing and sending
2. Encoding Issues
The signing payload must be encoded as UTF-8 bytes before signing. If your language defaults to a different encoding (e.g., Latin-1), the signature will not match.3. Including the Host in the Path
TheREQUEST_PATH component is the path and query string only. Do not include the
scheme or host:
4. Timestamp as String vs Integer
TheX-Timestamp header value is a string representation of a Unix timestamp in seconds
(not milliseconds). Make sure you are not accidentally sending milliseconds:
5. Stale Timestamps in Retry Logic
If your HTTP client automatically retries failed requests, make sure the retry logic recomputes the timestamp and re-signs the payload. Retrying with the original timestamp may cause the request to fall outside the tolerance window.6. Query Parameter Ordering
The query string in the signing payload must match the query string sent in the HTTP request exactly, character for character. If your HTTP library reorders query parameters, you must sign the reordered version.Building a Signing Helper
In production, you should extract signing into a reusable function or HTTP middleware. Here is a reference implementation:Verifying Your Implementation
Use these steps to verify your signing implementation is correct:- Start with sandbox. Sandbox uses the same authentication mechanism as production.
- Test a simple GET first.
GET /v1/entitieswith no query parameters is the simplest case. - Check error messages. If you get
invalid_signature, the server will include arequestId— contact support with this ID for debugging. - Compare signing payloads. Print the exact signing payload string (with escaped newlines visible) to verify each component is correct.
- Verify timestamp freshness. Ensure your timestamp is within the 60-second window.
Once your signing helper works for
GET /v1/entities and POST /v1/fx/quotes,
it will work for every endpoint in the API. The signing mechanism is uniform across
all 138 operations.Related
- Authentication Overview — Why OpenFX uses three-part authentication
- Key Management — Generating, storing, and rotating Ed25519 key pairs
- Idempotency — Required
X-Idempotency-Keyheader on state-changing requests - Error Handling — Full error response format and error types