Sign raw binary document uploads with the SHA-256 hash of the exact file bytes.
Signing binary document uploads
This page applies to production requests that use HMAC authentication. In sandbox, use bearer authentication and send the file normally.
These endpoints accept the file itself as the request body. Do not Base64-encode the file, put it in JSON, or wrap it in a form.
The same access key, timestamp, request path, signature algorithm, and Authorization header format described in Authentication apply here.
Supported endpoints:
PUT /v2/accounts/{main_account_id}/businesses/{sub_account_id}/documents/{document_type}PUT /v2/accounts/{main_account_id}/businesses/{sub_account_id}/controllers/{controller_id}/documents/{document_type}PUT /v2/accounts/{main_account_id}/individuals/{sub_account_id}/documents/{document_type}
Do not sign production requests in browser code. Send browser uploads through your server so the secret key remains server-side.
Complete-body signing
What is signed
Calculate the SHA-256 hash of the exact file bytes:
BODY_HASH = sha256_hex(FILE_BYTES)Build this exact canonical string:
<ACCESS_KEY>
<TIMESTAMP>
PUT
<PATH>
<BODY_HASH>Use one newline between fields and no trailing newline. PATH contains only the request path, without the domain or query string.
Decode SECRET_KEY from URL-safe Base64, calculate the HMAC-SHA256 of the canonical string, and encode the result as lowercase hexadecimal.
Required request sequence
- Read the document as bytes.
- Calculate
BODY_HASHfrom those bytes. - Build the canonical string with
PUTand the exact request path. - Calculate the HMAC-SHA256 signature.
- Add the authentication and document headers.
- Send the same file bytes without transforming them.
Required headers:
Authorization: OneMoney-HMAC-SHA256 <ACCESS_KEY>:<TIMESTAMP>:<SIGNATURE>
X-OM-Date: <TIMESTAMP>
Content-Type: <DOCUMENT_MIME_TYPE>
Content-Disposition: attachment; filename="<FILENAME>"The timestamp in the canonical string, Authorization, and X-OM-Date must be identical.
Complete-body example
The signing calculation is:
FILE_BYTES = read_file_as_bytes(FILE_PATH)
BODY_HASH = sha256_hex(FILE_BYTES)
STRING_TO_SIGN = join_with_newlines(
ACCESS_KEY,
TIMESTAMP,
"PUT",
PATH,
BODY_HASH
)
SIGNATURE = hmac_sha256_hex(base64url_decode(SECRET_KEY), STRING_TO_SIGN)Send the same file with the generated signature:
curl --request PUT \
--header "Authorization: OneMoney-HMAC-SHA256 $ACCESS_KEY:$TIMESTAMP:$SIGNATURE" \
--header "X-OM-Date: $TIMESTAMP" \
--header 'Content-Type: application/pdf' \
--header 'Content-Disposition: attachment; filename="document.pdf"' \
--data-binary '@/path/to/document.pdf' \
'https://api.1money.com/v2/accounts/BZ-RVED-ZREV/businesses/BZ-XYMU-9WKK/documents/MEMORANDUM_OF_ASSOCIATION'High-level HTTP libraries
The HTTP client must send the same bytes used to calculate BODY_HASH. If the client reads from a stream, hashes a different buffer, encodes the file, or compresses the body after signing, verification fails.
With cURL, use --data-binary @file to preserve the file bytes.
Troubleshooting signature mismatches
If a binary upload returns a signature mismatch, compare the file that was hashed with the body that was sent. Common causes are:
- The filename or local file path was hashed instead of the file contents.
- An empty body, Base64 string, or JSON representation was hashed.
- The file changed or was transformed between signing and sending.
- The method or request path in the canonical string differs from the request.
- The canonical string contains an extra newline.
- The timestamp in
Authorizationdiffers fromX-OM-Dateor is expired. - The secret key was not decoded from URL-safe Base64 before calculating the HMAC.
Log the file length and SHA-256 hash when diagnosing integration issues. Do not log document bytes, secrets, or the complete Authorization header.
